Add .gitignore and remove incomplete file
This commit is contained in:
+15
@@ -0,0 +1,15 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
venv/
|
||||
|
||||
# Uploads
|
||||
uploads/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
-663
@@ -1,663 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
IT Site Survey AI Application
|
||||
Complete implementation with all requested features:
|
||||
- Photo uploads for AI assessment
|
||||
- Model selection (Ollama vision models)
|
||||
- Export results (PDF, Text, Email)
|
||||
- Delete surveys (individual + Clear All)
|
||||
- Chat interface for AI interaction
|
||||
- Load Sample Data for testing
|
||||
- Comprehensive 35-question survey template
|
||||
"""
|
||||
|
||||
from flask import Flask, request, jsonify, send_file
|
||||
from flask_cors import CORS
|
||||
import os
|
||||
import json
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
import base64
|
||||
import requests
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app, resources={
|
||||
r"/api/*": {
|
||||
"origins": ["http://192.168.50.11:3003", "http://localhost:3003"],
|
||||
"methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
||||
"allow_headers": ["Content-Type", "Authorization"]
|
||||
}
|
||||
})
|
||||
|
||||
# Configuration
|
||||
UPLOAD_FOLDER = 'uploads'
|
||||
PHOTOS_FOLDER = 'uploads/photos'
|
||||
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
|
||||
OLLAMA_HOST = os.getenv('OLLAMA_HOST', 'http://192.168.19.25:11434')
|
||||
OLLAMA_URL = f"{OLLAMA_HOST}/api/chat"
|
||||
|
||||
# Create upload directories
|
||||
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
|
||||
os.makedirs(PHOTOS_FOLDER, exist_ok=True)
|
||||
|
||||
# In-memory storage
|
||||
surveys_db = {}
|
||||
responses_db = {}
|
||||
|
||||
# Survey Template with 35 comprehensive questions
|
||||
DEFAULT_SURVEY_TEMPLATE = {
|
||||
"title": "IT Infrastructure Site Survey",
|
||||
"description": "Comprehensive network infrastructure assessment",
|
||||
"questions": [
|
||||
# Site Topology & Layout (1-4)
|
||||
{
|
||||
"id": "q1",
|
||||
"category": "Site Topology & Layout",
|
||||
"question": "What is the total square footage of the site?",
|
||||
"type": "number",
|
||||
"placeholder": "e.g., 3500"
|
||||
},
|
||||
{
|
||||
"id": "q2",
|
||||
"category": "Site Topology & Layout",
|
||||
"question": "How many floors or buildings need network coverage?",
|
||||
"type": "number",
|
||||
"placeholder": "e.g., 2"
|
||||
},
|
||||
{
|
||||
"id": "q3",
|
||||
"category": "Site Topology & Layout",
|
||||
"question": "Describe the site layout (open plan, offices, warehouse, etc.)",
|
||||
"type": "textarea",
|
||||
"placeholder": "Describe the layout..."
|
||||
},
|
||||
{
|
||||
"id": "q4",
|
||||
"category": "Site Topology & Layout",
|
||||
"question": "Are there any physical barriers that may affect WiFi coverage?",
|
||||
"type": "multiselect",
|
||||
"options": ["Concrete walls", "Metal shelving", "Elevator shafts", "Thick drywall", "None"]
|
||||
},
|
||||
|
||||
# Network Closet & Equipment (5-8)
|
||||
{
|
||||
"id": "q5",
|
||||
"category": "Network Closet & Equipment",
|
||||
"question": "How many network closets or IDF rooms are on-site?",
|
||||
"type": "number",
|
||||
"placeholder": "e.g., 2"
|
||||
},
|
||||
{
|
||||
"id": "q6",
|
||||
"category": "Network Closet & Equipment",
|
||||
"question": "Describe the current network closet setup",
|
||||
"type": "textarea",
|
||||
"placeholder": "Describe the setup..."
|
||||
},
|
||||
{
|
||||
"id": "q7",
|
||||
"category": "Network Closet & Equipment",
|
||||
"question": "What type of power and cooling is available in the network closets?",
|
||||
"type": "textarea",
|
||||
"placeholder": "Describe power and cooling..."
|
||||
},
|
||||
{
|
||||
"id": "q8",
|
||||
"category": "Network Closet & Equipment",
|
||||
"question": "Are there existing server racks or wall-mounted equipment?",
|
||||
"type": "select",
|
||||
"options": ["Yes, full racks", "Yes, partial racks", "Wall-mounted only", "No existing racks"]
|
||||
},
|
||||
|
||||
# Cabling Infrastructure (9-12)
|
||||
{
|
||||
"id": "q9",
|
||||
"category": "Cabling Infrastructure",
|
||||
"question": "What type of existing cabling is in place?",
|
||||
"type": "select",
|
||||
"options": ["Cat5e (100Mb-1GbE)", "Cat6 (1GbE-10GbE)", "Cat6a (10GbE)", "Fiber (Multi-mode)", "Fiber (Single-mode)", "No existing cabling", "Unknown"]
|
||||
},
|
||||
{
|
||||
"id": "q10",
|
||||
"category": "Cabling Infrastructure",
|
||||
"question": "How many network drops are currently installed?",
|
||||
"type": "number",
|
||||
"placeholder": "e.g., 120"
|
||||
},
|
||||
{
|
||||
"id": "q11",
|
||||
"category": "Cabling Infrastructure",
|
||||
"question": "What is the estimated distance from the MDF to the farthest IDF or workstation?",
|
||||
"type": "textarea",
|
||||
"placeholder": "Describe distances..."
|
||||
},
|
||||
{
|
||||
"id": "q12",
|
||||
"category": "Cabling Infrastructure",
|
||||
"question": "Are there any special cabling requirements?",
|
||||
"type": "multiselect",
|
||||
"options": ["Plenum-rated cable required", "Outdoor-rated cable needed", "Direct burial cable needed", "Aerial cable needed", "Shielded cable required", "None"]
|
||||
},
|
||||
|
||||
# Network Equipment (13-15)
|
||||
{
|
||||
"id": "q13",
|
||||
"category": "Network Equipment",
|
||||
"question": "What network equipment is currently installed?",
|
||||
"type": "multiselect",
|
||||
"options": ["Router/Firewall", "Managed switches", "Unmanaged switches", "Wireless access points", "None/Unknown"]
|
||||
},
|
||||
{
|
||||
"id": "q14",
|
||||
"category": "Network Equipment",
|
||||
"question": "What type of switches are needed?",
|
||||
"type": "select",
|
||||
"options": ["Unmanaged (plug-and-play)", "Managed (Layer 2)", "Managed (Layer 3 with routing)", "PoE switches", "Industrial-grade switches"]
|
||||
},
|
||||
{
|
||||
"id": "q15",
|
||||
"category": "Network Equipment",
|
||||
"question": "How many wireless access points are needed?",
|
||||
"type": "number",
|
||||
"placeholder": "e.g., 6"
|
||||
},
|
||||
|
||||
# Power Requirements (16-18)
|
||||
{
|
||||
"id": "q16",
|
||||
"category": "Power Requirements",
|
||||
"question": "What is the estimated total power consumption for all network equipment?",
|
||||
"type": "textarea",
|
||||
"placeholder": "Describe power requirements..."
|
||||
},
|
||||
{
|
||||
"id": "q17",
|
||||
"category": "Power Requirements",
|
||||
"question": "What PoE devices will be connected?",
|
||||
"type": "multiselect",
|
||||
"options": ["IP Phones", "Wireless APs", "IP Cameras", "IoT sensors", "None", "Not sure"]
|
||||
},
|
||||
{
|
||||
"id": "q18",
|
||||
"category": "Power Requirements",
|
||||
"question": "What UPS backup capacity is required?",
|
||||
"type": "select",
|
||||
"options": ["No backup needed", "Minimal backup (15-30 min)", "Standard backup (1-2 hours)", "Extended backup (4+ hours)", "Generator backup"]
|
||||
},
|
||||
|
||||
# User & Device Requirements (19-21)
|
||||
{
|
||||
"id": "q19",
|
||||
"category": "User & Device Requirements",
|
||||
"question": "How many employees or users will use the network?",
|
||||
"type": "number",
|
||||
"placeholder": "e.g., 45"
|
||||
},
|
||||
{
|
||||
"id": "q20",
|
||||
"category": "User & Device Requirements",
|
||||
"question": "What is the estimated total number of devices?",
|
||||
"type": "number",
|
||||
"placeholder": "e.g., 85"
|
||||
},
|
||||
{
|
||||
"id": "q21",
|
||||
"category": "User & Device Requirements",
|
||||
"question": "What types of network activities will be performed?",
|
||||
"type": "multiselect",
|
||||
"options": ["Email/Web Browsing", "Video conferencing", "File sharing", "Cloud applications", "Remote desktop", "VoIP/Phone calls", "Streaming media"]
|
||||
},
|
||||
|
||||
# Security & Compliance (22-24)
|
||||
{
|
||||
"id": "q22",
|
||||
"category": "Security & Compliance",
|
||||
"question": "What compliance standards must be met?",
|
||||
"type": "multiselect",
|
||||
"options": ["HIPAA", "PCI-DSS", "SOC 2", "GDPR", "NIST", "None/General security"]
|
||||
},
|
||||
{
|
||||
"id": "q23",
|
||||
"category": "Security & Compliance",
|
||||
"question": "What network segmentation is required?",
|
||||
"type": "multiselect",
|
||||
"options": ["Corporate network", "Guest network", "IoT network", "Management network", "No segmentation needed"]
|
||||
},
|
||||
{
|
||||
"id": "q24",
|
||||
"category": "Security & Compliance",
|
||||
"question": "What security features are needed?",
|
||||
"type": "multiselect",
|
||||
"options": ["Firewall", "VPN access", "Intrusion detection", "Network access control (NAC)", "Content filtering", "None"]
|
||||
},
|
||||
|
||||
# Internet & WAN (25-27)
|
||||
{
|
||||
"id": "q25",
|
||||
"category": "Internet & WAN",
|
||||
"question": "What type of internet connectivity is available?",
|
||||
"type": "multiselect",
|
||||
"options": ["Fiber", "Cable/DSL", "5G/Cellular backup", "Dedicated leased line", "No existing connection"]
|
||||
},
|
||||
{
|
||||
"id": "q26",
|
||||
"category": "Internet & WAN",
|
||||
"question": "What is the required internet bandwidth?",
|
||||
"type": "textarea",
|
||||
"placeholder": "e.g., 500Mbps download / 250Mbps upload"
|
||||
},
|
||||
{
|
||||
"id": "q27",
|
||||
"category": "Internet & WAN",
|
||||
"question": "Are there multiple sites that need to be connected?",
|
||||
"type": "select",
|
||||
"options": ["No - Single site only", "Yes - 2-3 sites", "Yes - 4+ sites", "Not sure yet"]
|
||||
},
|
||||
|
||||
# Existing Infrastructure (28-30)
|
||||
{
|
||||
"id": "q28",
|
||||
"category": "Existing Infrastructure",
|
||||
"question": "Is there existing IT infrastructure to integrate with?",
|
||||
"type": "select",
|
||||
"options": ["Yes - Full upgrade needed", "Yes - Partial upgrade", "No - New installation", "Not sure"]
|
||||
},
|
||||
{
|
||||
"id": "q29",
|
||||
"category": "Existing Infrastructure",
|
||||
"question": "Describe any existing network issues or limitations",
|
||||
"type": "textarea",
|
||||
"placeholder": "Describe current issues..."
|
||||
},
|
||||
{
|
||||
"id": "q30",
|
||||
"category": "Existing Infrastructure",
|
||||
"question": "What performance issues have been observed?",
|
||||
"type": "multiselect",
|
||||
"options": ["Slow internet", "WiFi dead zones", "Network congestion", "Frequent disconnections", "No issues observed"]
|
||||
},
|
||||
|
||||
# Budget & Timeline (31-33)
|
||||
{
|
||||
"id": "q31",
|
||||
"category": "Budget & Timeline",
|
||||
"question": "What is the estimated budget for this project?",
|
||||
"type": "select",
|
||||
"options": ["Under $10,000", "$10,000 - $25,000", "$25,000 - $50,000", "$50,000 - $100,000", "$100,000+", "Not determined yet"]
|
||||
},
|
||||
{
|
||||
"id": "q32",
|
||||
"category": "Budget & Timeline",
|
||||
"question": "What is the desired timeline for completion?",
|
||||
"type": "select",
|
||||
"options": ["Within 1 month", "Within 3 months", "Within 6 months", "Within 1 year", "Flexible"]
|
||||
},
|
||||
{
|
||||
"id": "q33",
|
||||
"category": "Budget & Timeline",
|
||||
"question": "Who will be responsible for ongoing network management?",
|
||||
"type": "select",
|
||||
"options": ["Internal IT team", "Managed service provider (MSP)", "Hybrid approach", "Not determined yet"]
|
||||
},
|
||||
|
||||
# Additional Details (34-35)
|
||||
{
|
||||
"id": "q34",
|
||||
"category": "Additional Details",
|
||||
"question": "Upload any site photos, floor plans, or network diagrams",
|
||||
"type": "file",
|
||||
"placeholder": "Upload photos..."
|
||||
},
|
||||
{
|
||||
"id": "q35",
|
||||
"category": "Additional Details",
|
||||
"question": "Any additional requirements or special considerations?",
|
||||
"type": "textarea",
|
||||
"placeholder": "Describe any additional requirements..."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# Sample data for testing
|
||||
SAMPLE_SURVEY_DATA = {
|
||||
"name": "Sample Corporate Office Survey",
|
||||
"client_name": "Acme Corporation",
|
||||
"site_name": "Main Office - Downtown",
|
||||
"description": "Comprehensive network infrastructure assessment for downtown office location",
|
||||
"responses": {
|
||||
"q1": "3500",
|
||||
"q2": "2",
|
||||
"q3": "Single-story office building with open floor plan, 8 private offices, 2 conference rooms, break room, and reception area. Approximately 60 workstations in cubicle area.",
|
||||
"q4": ["Concrete walls"],
|
||||
"q5": "2",
|
||||
"q6": "MDF located in basement utility room. IDF on first floor in IT storage closet. Both have adequate ventilation but limited space.",
|
||||
"q7": "2x 20A circuits per closet, UPS backup planned for MDF",
|
||||
"q8": "Yes, partial racks",
|
||||
"q9": "Cat5e (100Mb-1GbE)",
|
||||
"q10": "120",
|
||||
"q11": "Average 150 feet, longest run approximately 280 feet to corner offices",
|
||||
"q12": ["Plenum-rated cable required"],
|
||||
"q13": ["Router/Firewall", "Unmanaged switches", "Wireless access points"],
|
||||
"q14": "Managed (Layer 2)",
|
||||
"q15": "6",
|
||||
"q16": "1800W estimated for all network equipment including PoE devices",
|
||||
"q17": ["IP Phones", "Wireless APs", "IP Cameras"],
|
||||
"q18": "Limited backup (1-2 hours)",
|
||||
"q19": "45",
|
||||
"q20": "85",
|
||||
"q21": ["Email/Web Browsing", "Video conferencing", "File sharing", "Cloud applications", "Remote desktop"],
|
||||
"q22": ["SOC 2"],
|
||||
"q23": ["Corporate network", "Guest network", "IoT network", "Management network"],
|
||||
"q24": ["Firewall", "VPN access", "Network access control (NAC)"],
|
||||
"q25": ["Fiber", "5G/Cellular backup"],
|
||||
"q26": "500Mbps download / 250Mbps upload",
|
||||
"q27": "No - Single site only",
|
||||
"q28": "Yes - Partial upgrade",
|
||||
"q29": "Current WiFi has dead zones in conference rooms. Existing switches are unmanaged and showing signs of age. Need better coverage and management capabilities.",
|
||||
"q30": ["Slow internet", "WiFi dead zones", "Network congestion"],
|
||||
"q31": "$25,000 - $50,000",
|
||||
"q32": "Within 3 months",
|
||||
"q33": "Internal IT team",
|
||||
"q34": "",
|
||||
"q35": "Need outdoor WiFi coverage for patio area. Plan to add IP cameras in future. Prefer Cisco or Ubiquiti equipment. Require weekend installation to minimize downtime."
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@app.route('/api/health', methods=['GET'])
|
||||
def health_check():
|
||||
"""Health check endpoint"""
|
||||
try:
|
||||
resp = requests.get(f"{OLLAMA_HOST}/api/tags", timeout=5)
|
||||
ollama_online = resp.status_code == 200
|
||||
except:
|
||||
ollama_online = False
|
||||
|
||||
return jsonify({
|
||||
"status": "ok",
|
||||
"service": "site-survey-ai",
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"ollama_connected": ollama_online
|
||||
})
|
||||
|
||||
|
||||
@app.route('/api/models', methods=['GET'])
|
||||
def get_models():
|
||||
"""Get available Ollama models"""
|
||||
try:
|
||||
resp = requests.get(f"{OLLAMA_HOST}/api/tags", timeout=5)
|
||||
if resp.status_code == 200:
|
||||
models_data = resp.json()
|
||||
models = [m['name'] for m in models_data.get('models', [])]
|
||||
# Add cloud models if available
|
||||
cloud_models = ['kimi-k2.6:cloud', 'kimi-k2.5:cloud', 'nemotron-3-super:cloud']
|
||||
models.extend(cloud_models)
|
||||
return jsonify({"models": models, "default": "kimi-k2.6:cloud"})
|
||||
except Exception as e:
|
||||
print(f"Error fetching models: {e}")
|
||||
|
||||
return jsonify({
|
||||
"models": ["kimi-k2.6:cloud", "kimi-k2.5:cloud", "llava:latest"],
|
||||
"default": "kimi-k2.6:cloud"
|
||||
})
|
||||
|
||||
|
||||
@app.route('/api/ollama-status', methods=['GET'])
|
||||
def ollama_status():
|
||||
"""Check Ollama connection status"""
|
||||
try:
|
||||
resp = requests.get(f"{OLLAMA_HOST}/api/tags", timeout=5)
|
||||
return jsonify({"online": resp.status_code == 200})
|
||||
except:
|
||||
return jsonify({"online": False})
|
||||
|
||||
|
||||
@app.route('/api/surveys/template', methods=['GET'])
|
||||
def get_survey_template():
|
||||
"""Get the survey template with questions"""
|
||||
return jsonify({"template": DEFAULT_SURVEY_TEMPLATE})
|
||||
|
||||
|
||||
@app.route('/api/surveys', methods=['GET'])
|
||||
def list_surveys():
|
||||
"""List all surveys"""
|
||||
surveys = []
|
||||
for sid, survey in surveys_db.items():
|
||||
surveys.append({
|
||||
"id": sid,
|
||||
"name": survey['name'],
|
||||
"client_name": survey.get('client_name', ''),
|
||||
"site_name": survey.get('site_name', ''),
|
||||
"description": survey.get('description', ''),
|
||||
"created_at": survey['created_at'],
|
||||
"responses_count": len(survey.get('responses', []))
|
||||
})
|
||||
return jsonify({"surveys": surveys})
|
||||
|
||||
|
||||
@app.route('/api/surveys', methods=['POST'])
|
||||
def create_survey():
|
||||
"""Create a new survey"""
|
||||
data = request.get_json()
|
||||
|
||||
survey_id = str(uuid.uuid4())
|
||||
survey = {
|
||||
'id': survey_id,
|
||||
'name': data.get('name', 'New Survey'),
|
||||
'description': data.get('description', ''),
|
||||
'client_name': data.get('client_name', ''),
|
||||
'site_name': data.get('site_name', ''),
|
||||
'questions': DEFAULT_SURVEY_TEMPLATE['questions'],
|
||||
'created_at': datetime.utcnow().isoformat(),
|
||||
'status': 'active',
|
||||
'responses': []
|
||||
}
|
||||
|
||||
# If responses are provided, add them
|
||||
responses_data = data.get('responses')
|
||||
if responses_data and isinstance(responses_data, dict) and len(responses_data) > 0:
|
||||
response_entry = {
|
||||
'id': str(uuid.uuid4()),
|
||||
'survey_id': survey_id,
|
||||
'responses': responses_data,
|
||||
'photos': [],
|
||||
'analyzed': False,
|
||||
'analysis': None,
|
||||
'submitter_name': 'Survey Creator',
|
||||
'created_at': datetime.utcnow().isoformat()
|
||||
}
|
||||
survey['responses'].append(response_entry)
|
||||
|
||||
surveys_db[survey_id] = survey
|
||||
return jsonify({"survey": survey}), 201
|
||||
|
||||
|
||||
@app.route('/api/surveys/<survey_id>', methods=['GET'])
|
||||
def get_survey(survey_id):
|
||||
"""Get a specific survey"""
|
||||
if survey_id not in surveys_db:
|
||||
return jsonify({"error": "Survey not found"}), 404
|
||||
return jsonify({"survey": surveys_db[survey_id]})
|
||||
|
||||
|
||||
@app.route('/api/surveys/<survey_id>', methods=['DELETE'])
|
||||
def delete_survey(survey_id):
|
||||
"""Delete a survey"""
|
||||
if survey_id not in surveys_db:
|
||||
return jsonify({"error": "Survey not found"}), 404
|
||||
|
||||
del surveys_db[survey_id]
|
||||
return jsonify({"success": True, "message": "Survey deleted"})
|
||||
|
||||
|
||||
@app.route('/api/surveys', methods=['DELETE'])
|
||||
def delete_all_surveys():
|
||||
"""Delete all surveys"""
|
||||
count = len(surveys_db)
|
||||
surveys_db.clear()
|
||||
return jsonify({"success": True, "message": f"Deleted {count} surveys"})
|
||||
|
||||
|
||||
@app.route('/api/surveys/<survey_id>/responses', methods=['POST'])
|
||||
def submit_response(survey_id):
|
||||
"""Submit survey responses"""
|
||||
if survey_id not in surveys_db:
|
||||
return jsonify({"error": "Survey not found"}), 404
|
||||
|
||||
data = request.get_json()
|
||||
response_id = str(uuid.uuid4())
|
||||
|
||||
response_entry = {
|
||||
'id': response_id,
|
||||
'survey_id': survey_id,
|
||||
'responses': data.get('responses', {}),
|
||||
'photos': data.get('photos', []),
|
||||
'analyzed': False,
|
||||
'analysis': None,
|
||||
'submitter_name': data.get('submitter_name', 'Anonymous'),
|
||||
'created_at': datetime.utcnow().isoformat()
|
||||
}
|
||||
|
||||
surveys_db[survey_id]['responses'].append(response_entry)
|
||||
return jsonify({"response": response_entry}), 201
|
||||
|
||||
|
||||
@app.route('/api/surveys/<survey_id>/photos', methods=['POST'])
|
||||
def upload_photo(survey_id):
|
||||
"""Upload a photo for a survey"""
|
||||
if survey_id not in surveys_db:
|
||||
return jsonify({"error": "Survey not found"}), 404
|
||||
|
||||
if 'photo' not in request.files:
|
||||
return jsonify({"error": "No photo provided"}), 400
|
||||
|
||||
photo = request.files['photo']
|
||||
if photo.filename == '':
|
||||
return jsonify({"error": "No photo selected"}), 400
|
||||
|
||||
# Save photo
|
||||
filename = secure_filename(f"{survey_id}_{uuid.uuid4()}_{photo.filename}")
|
||||
photo_path = os.path.join(PHOTOS_FOLDER, filename)
|
||||
photo.save(photo_path)
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"filename": filename,
|
||||
"url": f"/api/photos/{survey_id}/{filename}"
|
||||
})
|
||||
|
||||
|
||||
@app.route('/api/photos/<survey_id>/<filename>', methods=['GET'])
|
||||
def get_photo(survey_id, filename):
|
||||
"""Get a photo"""
|
||||
photo_path = os.path.join(PHOTOS_FOLDER, filename)
|
||||
if os.path.exists(photo_path):
|
||||
return send_file(photo_path)
|
||||
return jsonify({"error": "Photo not found"}), 404
|
||||
|
||||
|
||||
@app.route('/api/surveys/<survey_id>/analyze', methods=['POST'])
|
||||
def analyze_survey(survey_id):
|
||||
"""Analyze survey with AI"""
|
||||
if survey_id not in surveys_db:
|
||||
return jsonify({"error": "Survey not found"}), 404
|
||||
|
||||
survey = surveys_db[survey_id]
|
||||
data = request.get_json() or {}
|
||||
selected_model = data.get('model', 'llava:latest')
|
||||
|
||||
# Get the latest response
|
||||
if not survey['responses']:
|
||||
return jsonify({"error": "No responses to analyze"}), 400
|
||||
|
||||
last_response = survey['responses'][-1]
|
||||
responses = last_response['responses']
|
||||
|
||||
# Build analysis prompt
|
||||
prompt = f"""Analyze this IT site survey and provide recommendations:
|
||||
|
||||
Survey: {survey['name']}
|
||||
Client: {survey['client_name']}
|
||||
Site: {survey['site_name']}
|
||||
|
||||
Responses:
|
||||
"""
|
||||
|
||||
for q in survey['questions']:
|
||||
qid = q['id']
|
||||
if qid in responses:
|
||||
prompt += f"\n{q['question']}: {responses[qid]}"
|
||||
|
||||
prompt += "\n\nProvide a detailed analysis including:\n"
|
||||
prompt += "1. Network infrastructure recommendations\n"
|
||||
prompt += "2. Equipment recommendations with estimated costs\n"
|
||||
prompt += "3. Implementation timeline\n"
|
||||
prompt += "4. Potential challenges and solutions\n"
|
||||
|
||||
try:
|
||||
# Call Ollama for analysis
|
||||
ollama_data = {
|
||||
"model": selected_model,
|
||||
"messages": [{"role": "user", "content": prompt}],
|
||||
"stream": False
|
||||
}
|
||||
|
||||
resp = requests.post(OLLAMA_URL, json=ollama_data, timeout=300)
|
||||
if resp.status_code == 200:
|
||||
result = resp.json()
|
||||
analysis = result.get('message', {}).get('content', 'No analysis available')
|
||||
|
||||
# Save analysis
|
||||
last_response['analyzed'] = True
|
||||
last_response['analysis'] = analysis
|
||||
last_response['model_used'] = selected_model
|
||||
|
||||
return jsonify({
|
||||
"analysis": analysis,
|
||||
"model_used": selected_model
|
||||
})
|
||||
else:
|
||||
return jsonify({"error": f"Ollama error: {resp.status_code}"}), 500
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/api/surveys/<survey_id>/export', methods=['POST'])
|
||||
def export_survey(survey_id):
|
||||
"""Export survey results"""
|
||||
if survey_id not in surveys_db:
|
||||
return jsonify({"error": "Survey not found"}), 404
|
||||
|
||||
data = request.get_json() or {}
|
||||
export_format = data.get('format', 'json')
|
||||
|
||||
survey = surveys_db[survey_id]
|
||||
|
||||
if export_format == 'json':
|
||||
return jsonify({"survey": survey})
|
||||
elif export_format == 'text':
|
||||
# Generate text report
|
||||
text = f"IT Site Survey Report\n"
|
||||
text += f"Survey: {survey['name']}\n"
|
||||
text += f"Client: {survey['client_name']}\n"
|
||||
text += f"Site: {survey['site_name']}\n"
|
||||
text += f"Date: {survey['created_at']}\n\n"
|
||||
|
||||
for response in survey['responses']:
|
||||
text += f"Response by: {response['submitter_name']}\n"
|
||||
for q in survey['questions']:
|
||||
qid = q['id']
|
||||
if qid in response['responses']:
|
||||
text += f"{q['question']}: {response['responses'][qid]}\n"
|
||||
if response.get('analysis'):
|
||||
text += f"\nAI Analysis:\n{response['analysis']}\n"
|
||||
|
||||
return jsonify({"text": text})
|
||||
else:
|
||||
return jsonify({"error": "Unsupported format"}), 400
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=3003, debug=False)
|
||||
Reference in New Issue
Block a user