Athspi commited on
Commit
9759c17
·
verified ·
1 Parent(s): d1937f0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +222 -23
app.py CHANGED
@@ -1,40 +1,239 @@
1
- from flask import Flask, render_template, request, session, redirect, url_for
2
  import os
3
- from flask_session import Session
 
 
 
4
 
 
 
 
 
 
 
 
 
 
 
5
  app = Flask(__name__)
6
- app.secret_key = 'supersecretkey'
7
- app.config['SESSION_TYPE'] = 'filesystem'
8
- Session(app)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  @app.route('/')
11
  def index():
 
 
 
 
 
 
12
  return render_template('index.html')
13
 
14
- @app.route('/chat', methods=['GET', 'POST'])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  def chat():
16
- # Simulate chat history
17
- if 'chat_history' not in session:
18
- session['chat_history'] = []
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
- if 'project_tree' not in session:
21
- session['project_tree'] = ['main.py', 'requirements.txt', 'templates/index.html']
22
 
23
- ai_reply = ''
24
- if request.method == 'POST':
25
- user_message = request.form.get('user_message')
26
- ai_reply = "Simulated AI response to: " + user_message
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
- session['chat_history'].append({'user': user_message, 'ai': ai_reply})
 
29
 
30
- return render_template('chat.html',
31
- chat_history=session.get('chat_history', []),
32
- project_tree=session.get('project_tree', []),
33
- ai_reply=ai_reply)
34
 
35
- @app.route('/download_project')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  def download_project():
37
- return "Simulated download"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
  if __name__ == '__main__':
40
- app.run(host="0.0.0.0", port=7860)
 
 
1
  import os
2
+ import zipfile
3
+ import uuid
4
+ import google.generativeai as genai
5
+ from flask import Flask, request, jsonify, render_template, session, send_from_directory
6
 
7
+ # --- Gemini API Configuration ---
8
+ # Load the API key from the .env file
9
+ from dotenv import load_dotenv
10
+ load_dotenv()
11
+
12
+ # Configure the Gemini API client
13
+ genai.configure(api_key=os.environ.get("GEMINI_API_KEY"))
14
+ model = genai.GenerativeModel('gemini-1.5-flash') # Or another suitable model
15
+
16
+ # Initialize the Flask app
17
  app = Flask(__name__)
18
+
19
+ # Configure the upload folder
20
+ UPLOAD_FOLDER = 'uploads'
21
+ app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
22
+
23
+ if not os.path.exists(UPLOAD_FOLDER):
24
+ os.makedirs(UPLOAD_FOLDER)
25
+
26
+ def get_project_files(project_path):
27
+ """
28
+ Gets the file tree and content of all files in the project directory.
29
+ """
30
+ file_data = {}
31
+ for root, _, files in os.walk(project_path):
32
+ for file in files:
33
+ file_path = os.path.join(root, file)
34
+ relative_path = os.path.relpath(file_path, project_path)
35
+ try:
36
+ with open(file_path, 'r', encoding='utf-8') as f:
37
+ content = f.read()
38
+ file_data[relative_path] = content
39
+ except Exception as e:
40
+ file_data[relative_path] = f"Error reading file: {e}"
41
+ return file_data
42
 
43
  @app.route('/')
44
  def index():
45
+ """
46
+ Renders the main page.
47
+ """
48
+ if 'project_id' not in session:
49
+ session['project_id'] = str(uuid.uuid4())
50
+ session['chat_history'] = []
51
  return render_template('index.html')
52
 
53
+ @app.route('/upload', methods=['POST'])
54
+ def upload_project():
55
+ """
56
+ Handles the project zip file upload.
57
+ """
58
+ if 'project_zip' not in request.files:
59
+ return jsonify({"error": "No file part"}), 400
60
+ file = request.files['project_zip']
61
+ if file.filename == '':
62
+ return jsonify({"error": "No selected file"}), 400
63
+ if file and file.filename.endswith('.zip'):
64
+ project_id = session['project_id']
65
+ project_path = os.path.join(app.config['UPLOAD_FOLDER'], project_id)
66
+ if not os.path.exists(project_path):
67
+ os.makedirs(project_path)
68
+
69
+ zip_path = os.path.join(project_path, file.filename)
70
+ file.save(zip_path)
71
+
72
+ with zipfile.ZipFile(zip_path, 'r') as zip_ref:
73
+ zip_ref.extractall(project_path)
74
+ os.remove(zip_path)
75
+
76
+ project_files = get_project_files(project_path)
77
+ session['project_files'] = project_files
78
+
79
+ # Initial context for the AI
80
+ initial_context = "The user has uploaded a new project. Here are the files and their content:\n\n"
81
+ for path, content in project_files.items():
82
+ initial_context += f"**File:** `{path}`\n```\n{content}\n```\n\n"
83
+
84
+ # Initialize chat history for Gemini
85
+ session['chat_history'] = [
86
+ {"role": "user", "parts": [initial_context]},
87
+ {"role": "model", "parts": ["I have understood the project structure and content. How can I help you improve it?"]}
88
+ ]
89
+
90
+ # Prepare response for the frontend (needs 'role' and 'content' keys)
91
+ frontend_chat_history = [
92
+ {"role": "user", "content": initial_context},
93
+ {"role": "assistant", "content": "I have understood the project structure and content. How can I help you improve it?"}
94
+ ]
95
+
96
+ return jsonify({
97
+ "message": "Project uploaded and understood.",
98
+ "file_tree": list(project_files.keys()),
99
+ "chat_history": frontend_chat_history
100
+ })
101
+ return jsonify({"error": "Invalid file type, please upload a .zip file"}), 400
102
+
103
+ @app.route('/chat', methods=['POST'])
104
  def chat():
105
+ """
106
+ Handles the chat interaction with the AI.
107
+ """
108
+ user_message = request.json.get('message')
109
+ if not user_message:
110
+ return jsonify({"error": "Empty message"}), 400
111
+
112
+ project_id = session.get('project_id')
113
+ if not project_id:
114
+ return jsonify({"error": "No project uploaded"}), 400
115
+
116
+ project_path = os.path.join(app.config['UPLOAD_FOLDER'], project_id)
117
+
118
+ # Add user message to chat history
119
+ session['chat_history'].append({"role": "user", "parts": [user_message]})
120
+ session.modified = True
121
 
122
+ # Start a chat session with the model using the history
123
+ chat_session = model.start_chat(history=session['chat_history'])
124
 
125
+ try:
126
+ # System instruction for Gemini
127
+ system_instruction = """You are an expert programmer AI assistant. When the user asks you to make changes to the code, you must respond with the file path and the complete, updated code for that file within ```python ... ```, ```html ... ```, or other appropriate markdown code blocks. For creating a new file, respond with the new file path and the content. For deleting a file, respond with 'DELETE: [file_path]'. For suggesting improvements with diffs, provide the original and suggested code in separate blocks.
128
+ Example for modifying a file:
129
+ `app/main.py`
130
+ ```python
131
+ # updated python code here
132
+ ```
133
+ Example for creating a file:
134
+ `static/css/new_style.css`
135
+ ```css
136
+ /* new css code */
137
+ ```
138
+ """
139
+ # We combine the system instruction with the user message for better context
140
+ full_prompt = f"{system_instruction}\n\nUser Question: {user_message}"
141
 
142
+ response = chat_session.send_message(full_prompt)
143
+ ai_response = response.text
144
 
145
+ # Add AI response to chat history
146
+ session['chat_history'].append({"role": "model", "parts": [ai_response]})
147
+ session.modified = True
 
148
 
149
+
150
+ # --- AI File Operation Logic (remains the same) ---
151
+ if "```" in ai_response:
152
+ lines = ai_response.split('\n')
153
+ file_path_line = lines[0].strip()
154
+ if file_path_line.startswith('`') and file_path_line.endswith('`'):
155
+ file_path = file_path_line.strip('`')
156
+ try:
157
+ content_start = ai_response.find('```') + 3
158
+ content_end = ai_response.rfind('```')
159
+ # Skip the language hint (e.g., python, html)
160
+ code_content = ai_response[content_start:].split('\n', 1)[1]
161
+ code_content = code_content[:content_end - content_start - len(lines[1]) - 1]
162
+
163
+ full_path = os.path.join(project_path, file_path)
164
+ os.makedirs(os.path.dirname(full_path), exist_ok=True)
165
+ with open(full_path, 'w', encoding='utf-8') as f:
166
+ f.write(code_content)
167
+ except IndexError:
168
+ pass # Or handle error where code block is malformed
169
+
170
+ elif ai_response.startswith("DELETE:"):
171
+ file_to_delete = ai_response.split(":", 1)[1].strip()
172
+ full_path = os.path.join(project_path, file_to_delete)
173
+ if os.path.exists(full_path):
174
+ os.remove(full_path)
175
+
176
+ return jsonify({"reply": ai_response})
177
+
178
+ except Exception as e:
179
+ return jsonify({"error": str(e)}), 500
180
+
181
+
182
+ # --- Other routes (/file_tree, /file_content, /download) remain unchanged ---
183
+ # (You can copy them directly from the previous response)
184
+
185
+ @app.route('/file_tree')
186
+ def get_file_tree():
187
+ project_id = session.get('project_id')
188
+ if not project_id:
189
+ return jsonify({"error": "No project uploaded"}), 400
190
+ project_path = os.path.join(app.config['UPLOAD_FOLDER'], project_id)
191
+ if not os.path.exists(project_path):
192
+ return jsonify({"error": "Project not found"}), 404
193
+ file_tree = list(get_project_files(project_path).keys())
194
+ return jsonify({"file_tree": file_tree})
195
+
196
+
197
+ @app.route('/file_content')
198
+ def get_file_content():
199
+ project_id = session.get('project_id')
200
+ file_path = request.args.get('path')
201
+ if not project_id or not file_path:
202
+ return jsonify({"error": "Missing project or file path"}), 400
203
+
204
+ full_path = os.path.join(app.config['UPLOAD_FOLDER'], project_id, file_path)
205
+ if not os.path.exists(full_path):
206
+ return jsonify({"error": "File not found"}), 404
207
+
208
+ try:
209
+ with open(full_path, 'r', encoding='utf-8') as f:
210
+ content = f.read()
211
+ return jsonify({"path": file_path, "content": content})
212
+ except Exception as e:
213
+ return jsonify({"error": str(e)}), 500
214
+
215
+
216
+ @app.route('/download')
217
  def download_project():
218
+ """
219
+ Zips and sends the current project state for download.
220
+ """
221
+ project_id = session.get('project_id')
222
+ if not project_id:
223
+ return "No project to download", 404
224
+
225
+ project_path = os.path.join(app.config['UPLOAD_FOLDER'], project_id)
226
+ zip_path = os.path.join(app.config['UPLOAD_FOLDER'], f"{project_id}.zip")
227
+
228
+ with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
229
+ for root, _, files in os.walk(project_path):
230
+ for file in files:
231
+ file_path = os.path.join(root, file)
232
+ arcname = os.path.relpath(file_path, project_path)
233
+ zipf.write(file_path, arcname)
234
+
235
+ return send_from_directory(app.config['UPLOAD_FOLDER'], f"{project_id}.zip", as_attachment=True)
236
+
237
 
238
  if __name__ == '__main__':
239
+ app.run(debug=True, port=5001)