md for database as it was fucked up in windows script
This commit is contained in:
@@ -3,3 +3,5 @@ Flask-SQLAlchemy>=3.1
|
|||||||
Markdown>=3.6
|
Markdown>=3.6
|
||||||
MarkupSafe>=2.1
|
MarkupSafe>=2.1
|
||||||
watchdog>=4.0
|
watchdog>=4.0
|
||||||
|
gunicorn>=23.0.0
|
||||||
|
waitress>=3.0.2
|
||||||
14
run.bash
14
run.bash
@@ -1,17 +1,25 @@
|
|||||||
u!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -e # exit if any command fails
|
set -e # exit if any command fails
|
||||||
|
|
||||||
# Ensure QPP/database directory exists
|
# Ensure src/database directory exists
|
||||||
mkdir -p src/database
|
mkdir -p src/database
|
||||||
|
|
||||||
|
# Create virtual environment if it doesn't exist
|
||||||
|
if [ ! -d "venv" ]; then
|
||||||
python -m venv venv
|
python -m venv venv
|
||||||
|
fi
|
||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
|
|
||||||
|
# Upgrade pip and install dependencies
|
||||||
pip install --upgrade pip
|
pip install --upgrade pip
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Export environment variables
|
||||||
export FLASK_APP=src.app
|
export FLASK_APP=src.app
|
||||||
export FLASK_ENV=production
|
export FLASK_ENV=production
|
||||||
|
|
||||||
flask run --host=0.0.0.0 --port=5000
|
# Run with Gunicorn
|
||||||
|
echo "Starting Flask app with Gunicorn..."
|
||||||
|
exec gunicorn -w 4 -b 0.0.0.0:5000 src.app:app
|
||||||
|
|
||||||
|
|||||||
4
run.bat
4
run.bat
@@ -1 +1,3 @@
|
|||||||
python -m flask --app .\src\app.py run --host=0.0.0.0 --port=5000
|
:: make db directory and then launch the server
|
||||||
|
md .\src\database
|
||||||
|
python -m waitress --listen=0.0.0.0:8000 src.app:app
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ app = Flask(__name__)
|
|||||||
BASE_DIR = Path(__file__).parent
|
BASE_DIR = Path(__file__).parent
|
||||||
app.config['SQLALCHEMY_DATABASE_URI'] = f"sqlite:///{BASE_DIR / 'database' / 'db.sqlite3'}"
|
app.config['SQLALCHEMY_DATABASE_URI'] = f"sqlite:///{BASE_DIR / 'database' / 'db.sqlite3'}"
|
||||||
|
|
||||||
print(f">>>>>>>>>>>>>>>>>>>>< Using database URI: {app.config['SQLALCHEMY_DATABASE_URI']}")
|
print(f"[ INFO ] : Using database URI: {app.config['SQLALCHEMY_DATABASE_URI']}")
|
||||||
|
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
|
|
||||||
|
|||||||
@@ -77,9 +77,9 @@ class ProblemScannerThread(threading.Thread):
|
|||||||
'test_code': test_code,
|
'test_code': test_code,
|
||||||
'difficulty': difficulty
|
'difficulty': difficulty
|
||||||
})
|
})
|
||||||
print(f"Found problem: {folder.name} ; Difficulty: {difficulty}")
|
print(f"[ INFO ]: Found problem: {folder.name} ; Difficulty: {difficulty}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error reading problem files for {folder.name}: {e}")
|
print(f"[ ERROR ]: Error reading problem files for {folder.name}: {e}")
|
||||||
else:
|
else:
|
||||||
missing_files = []
|
missing_files = []
|
||||||
if not manifest_path:
|
if not manifest_path:
|
||||||
@@ -88,9 +88,9 @@ class ProblemScannerThread(threading.Thread):
|
|||||||
missing_files.append("description.md")
|
missing_files.append("description.md")
|
||||||
if not test_path.exists():
|
if not test_path.exists():
|
||||||
missing_files.append("test.py")
|
missing_files.append("test.py")
|
||||||
print(f"Skipping {folder.name}: missing {', '.join(missing_files)}")
|
print(f"[ SKIP ]: Skipping {folder.name}: missing {', '.join(missing_files)}")
|
||||||
|
|
||||||
print(f"Total problems found: {len(problems)}")
|
print(f"[ INFO ]: Total problems found: {len(problems)}")
|
||||||
return problems
|
return problems
|
||||||
|
|
||||||
def update_db(self, problems, retries=5):
|
def update_db(self, problems, retries=5):
|
||||||
@@ -111,7 +111,7 @@ class ProblemScannerThread(threading.Thread):
|
|||||||
(p['folder'], p['description'], p['difficulty'], p['test_code']))
|
(p['folder'], p['description'], p['difficulty'], p['test_code']))
|
||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
print(f"Updated database with {len(problems)} problems")
|
print(f"[ INFO ]: Updated database with {len(problems)} problems")
|
||||||
conn.close()
|
conn.close()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -121,37 +121,37 @@ class ProblemScannerThread(threading.Thread):
|
|||||||
print(f"Database locked, retrying in {wait_time:.2f}s (attempt {attempt + 1})")
|
print(f"Database locked, retrying in {wait_time:.2f}s (attempt {attempt + 1})")
|
||||||
time.sleep(wait_time)
|
time.sleep(wait_time)
|
||||||
else:
|
else:
|
||||||
print(f"Database error: {e}")
|
print(f"[ ERROR ]: Database error: {e}")
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Unexpected error updating database: {e}")
|
print(f"[ ERROR ]: Unexpected error updating database: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
print('Failed to update problems DB after several retries due to lock.')
|
print('[ FATAL ERROR ]: Failed to update problems DB after several retries due to lock.')
|
||||||
|
|
||||||
def rescan_and_update(self):
|
def rescan_and_update(self):
|
||||||
print("Scanning for problems...")
|
print("[ INFO ]: Scanning for problems...")
|
||||||
problems = self.scan()
|
problems = self.scan()
|
||||||
self.update_db(problems)
|
self.update_db(problems)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
print("Starting problem scanner...")
|
print("[ INFO ]: Starting problem scanner...")
|
||||||
|
|
||||||
# Initial scan and table creation
|
# Initial scan and table creation
|
||||||
try:
|
try:
|
||||||
conn = sqlite3.connect(DB_PATH)
|
conn = sqlite3.connect(DB_PATH)
|
||||||
self.create_table(conn)
|
self.create_table(conn)
|
||||||
conn.close()
|
conn.close()
|
||||||
print("Database initialized")
|
print("[ INFO ]: Database initialized")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to initialize database: {e}")
|
print(f"[ FATAL ERROR ]: Failed to initialize database: {e}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Initial scan
|
# Initial scan
|
||||||
self.rescan_and_update()
|
self.rescan_and_update()
|
||||||
|
|
||||||
if WATCHDOG_AVAILABLE:
|
if WATCHDOG_AVAILABLE:
|
||||||
print("Using watchdog for file monitoring")
|
print("[ INFO ]: Using watchdog for file monitoring")
|
||||||
|
|
||||||
class Handler(FileSystemEventHandler):
|
class Handler(FileSystemEventHandler):
|
||||||
def __init__(self, scanner):
|
def __init__(self, scanner):
|
||||||
@@ -163,7 +163,7 @@ class ProblemScannerThread(threading.Thread):
|
|||||||
now = time.time()
|
now = time.time()
|
||||||
if now - self.last_event_time > 1: # Wait at least 1 second between rescans
|
if now - self.last_event_time > 1: # Wait at least 1 second between rescans
|
||||||
self.last_event_time = now
|
self.last_event_time = now
|
||||||
print(f"File system event: {event.event_type} - {event.src_path}")
|
print(f"[ FSINFO ]: File system event: {event.event_type} - {event.src_path}")
|
||||||
self.scanner.rescan_and_update()
|
self.scanner.rescan_and_update()
|
||||||
|
|
||||||
event_handler = Handler(self)
|
event_handler = Handler(self)
|
||||||
@@ -175,19 +175,19 @@ class ProblemScannerThread(threading.Thread):
|
|||||||
while True:
|
while True:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("Stopping problem scanner...")
|
print("[ KBINT_INFO ]: Stopping problem scanner...")
|
||||||
finally:
|
finally:
|
||||||
self.observer.stop()
|
self.observer.stop()
|
||||||
self.observer.join()
|
self.observer.join()
|
||||||
else:
|
else:
|
||||||
print(f"Watchdog not available, using polling every {self.scan_interval}s")
|
print(f"[ WARNING ]: Watchdog not available, using polling every {self.scan_interval}s")
|
||||||
# Fallback: poll every scan_interval seconds
|
# Fallback: poll every scan_interval seconds
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
time.sleep(self.scan_interval)
|
time.sleep(self.scan_interval)
|
||||||
self.rescan_and_update()
|
self.rescan_and_update()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("Stopping problem scanner...")
|
print("[ KBINT_INFO ]: Stopping problem scanner...")
|
||||||
|
|
||||||
def start_problem_scanner():
|
def start_problem_scanner():
|
||||||
scanner = ProblemScannerThread()
|
scanner = ProblemScannerThread()
|
||||||
@@ -198,14 +198,15 @@ def start_problem_scanner():
|
|||||||
def load_problems_from_json(json_path):
|
def load_problems_from_json(json_path):
|
||||||
"""Load problems from JSON file into Flask database"""
|
"""Load problems from JSON file into Flask database"""
|
||||||
if not os.path.exists(json_path):
|
if not os.path.exists(json_path):
|
||||||
print(f"Problem JSON file not found: {json_path}")
|
print(f"[ DEPRECATED_INFO ]: Problem JSON file not found: {json_path}")
|
||||||
|
print("[ SUGGESTION ]: If you dont have this do not worry. Use mainfest.json!")
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(json_path, 'r', encoding='utf-8') as f:
|
with open(json_path, 'r', encoding='utf-8') as f:
|
||||||
problems = json.load(f)
|
problems = json.load(f)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error reading JSON file: {e}")
|
print(f"[ ERROR ]: Error reading JSON file: {e}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# This assumes you have imported the necessary Flask/SQLAlchemy components
|
# This assumes you have imported the necessary Flask/SQLAlchemy components
|
||||||
@@ -223,24 +224,24 @@ def load_problems_from_json(json_path):
|
|||||||
with open(p['solution'], 'r', encoding='utf-8') as sf:
|
with open(p['solution'], 'r', encoding='utf-8') as sf:
|
||||||
test_code = sf.read()
|
test_code = sf.read()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error reading solution file for {p['title']}: {e}")
|
print(f"[ FATAL ERROR ]: Error reading solution file for {p['title']}: {e}")
|
||||||
|
|
||||||
if existing:
|
if existing:
|
||||||
existing.description = p['description']
|
existing.description = p['description']
|
||||||
existing.test_code = test_code
|
existing.test_code = test_code
|
||||||
print(f"Updated problem: {p['title']}")
|
print(f"[ INFO ]: Updated problem: {p['title']}")
|
||||||
else:
|
else:
|
||||||
new_problem = Problem(title=p['title'], description=p['description'], test_code=test_code)
|
new_problem = Problem(title=p['title'], description=p['description'], test_code=test_code)
|
||||||
db.session.add(new_problem)
|
db.session.add(new_problem)
|
||||||
print(f"Added new problem: {p['title']}")
|
print(f"[ SUCCESS ]: Added new problem: {p['title']}")
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
print("Successfully updated problems from JSON")
|
print("[ SUCCESS ]: Successfully updated problems from JSON")
|
||||||
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print("Flask models not available - skipping JSON load")
|
print("[ FATAL IMPORT ERROR ]: Flask models not available - skipping JSON load @execptImportError")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error loading problems from JSON: {e}")
|
print(f"[ ERROR ]: Error loading problems from JSON: {e}")
|
||||||
|
|
||||||
def schedule_problem_reload(app, json_path, interval_hours=10):
|
def schedule_problem_reload(app, json_path, interval_hours=10):
|
||||||
"""Schedule periodic reloading of problems from JSON"""
|
"""Schedule periodic reloading of problems from JSON"""
|
||||||
@@ -251,7 +252,7 @@ def schedule_problem_reload(app, json_path, interval_hours=10):
|
|||||||
load_problems_from_json(json_path)
|
load_problems_from_json(json_path)
|
||||||
time.sleep(interval_hours * 3600)
|
time.sleep(interval_hours * 3600)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error in problem reload loop: {e}")
|
print(f"[ FATAL ERROR ]: Error in problem reload loop: {e}")
|
||||||
time.sleep(60) # Wait 1 minute before retrying
|
time.sleep(60) # Wait 1 minute before retrying
|
||||||
|
|
||||||
t = threading.Thread(target=reload_loop, daemon=True)
|
t = threading.Thread(target=reload_loop, daemon=True)
|
||||||
@@ -366,7 +367,7 @@ def run_code_against_tests(user_code, test_code, timeout=10):
|
|||||||
try:
|
try:
|
||||||
os.unlink(temp_file)
|
os.unlink(temp_file)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Warning: Could not delete temp file {temp_file}: {e}")
|
print(f"[ FATAL WARNING ]: Could not delete temp file {temp_file}: {e}")
|
||||||
|
|
||||||
runtime = time.perf_counter() - start_time
|
runtime = time.perf_counter() - start_time
|
||||||
|
|
||||||
@@ -377,7 +378,7 @@ def run_code_against_tests(user_code, test_code, timeout=10):
|
|||||||
'error': error if not passed else None
|
'error': error if not passed else None
|
||||||
}
|
}
|
||||||
|
|
||||||
print(f"Test execution result: passed={passed}, runtime={runtime:.3f}s")
|
print(f"[ TEST RESULT ]: passed={passed}, runtime={runtime:.3f}s")
|
||||||
if error:
|
if error:
|
||||||
print(f"Error: {error}")
|
print(f"Error: {error}")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user