euler314 commited on
Commit
5720959
·
verified ·
1 Parent(s): c2cd346

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +203 -131
app.py CHANGED
@@ -1,4 +1,5 @@
1
- from flask import Flask, request, jsonify, render_template_string
 
2
  from google.oauth2.credentials import Credentials
3
  from googleapiclient.discovery import build
4
  from googleapiclient.http import MediaIoBaseUpload
@@ -7,154 +8,226 @@ import io
7
  import datetime
8
 
9
  app = Flask(__name__)
 
10
 
11
  SPREADSHEET_ID = '1rcIJflbC1VIc70F6dnASLFvGQ90TXHhCx0iyxsXR7Ww'
12
  FOLDER_ID = '1brmLNqOMCvRS0TDY6ECHvIBmlRx8pcPz'
13
 
14
- # OAuth 2.0 credentials
15
- CREDENTIALS = {
16
- 'token': None,
17
- 'refresh_token': None,
18
- 'token_uri': 'https://oauth2.googleapis.com/token',
19
- 'client_id': os.environ.get('GOOGLE_CLIENT_ID'),
20
- 'client_secret': os.environ.get('GOOGLE_CLIENT_SECRET'),
21
- 'scopes': ['https://www.googleapis.com/auth/spreadsheets',
22
- 'https://www.googleapis.com/auth/drive.file']
23
  }
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  HTML_TEMPLATE = '''
26
- <!DOCTYPE html>
27
- <html>
28
- <head>
29
- <title>Purchase Record Form</title>
30
- <meta charset="UTF-8">
31
- <style>
32
- body {
33
- font-family: Arial, sans-serif;
34
- max-width: 600px;
35
- margin: 20px auto;
36
- padding: 20px;
37
- }
38
- .form-group {
39
- margin-bottom: 20px;
40
- }
41
- label {
42
- display: block;
43
- margin-bottom: 8px;
44
- font-weight: bold;
45
- }
46
- input {
47
- width: 100%;
48
- padding: 10px;
49
- border: 1px solid #ddd;
50
- border-radius: 4px;
51
- font-size: 16px;
52
- }
53
- button {
54
- background-color: #4CAF50;
55
- color: white;
56
- padding: 12px 24px;
57
- border: none;
58
- border-radius: 4px;
59
- cursor: pointer;
60
- font-size: 16px;
61
- width: 100%;
62
- }
63
- button:hover {
64
- background-color: #45a049;
65
- }
66
- #status {
67
- margin-top: 20px;
68
- padding: 15px;
69
- border-radius: 4px;
70
- display: none;
71
- }
72
- .success {
73
- background-color: #dff0d8;
74
- color: #3c763d;
75
- }
76
- .error {
77
- background-color: #f2dede;
78
- color: #a94442;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  }
80
- </style>
81
- </head>
82
- <body>
83
- <h2>購買記錄表單 Purchase Record Form</h2>
84
- <form id="purchaseForm">
85
- <div class="form-group">
86
- <label for="name">姓名 Name:</label>
87
- <input type="text" id="name" name="name" required>
88
- </div>
89
 
90
- <div class="form-group">
91
- <label for="studentId">學號 Student ID:</label>
92
- <input type="text" id="studentId" name="studentId" required>
93
- </div>
 
94
 
95
- <div class="form-group">
96
- <label for="product">購買商品 Product:</label>
97
- <input type="text" id="product" name="product" required>
98
- </div>
 
99
 
100
- <div class="form-group">
101
- <label for="cost">金額 Cost:</label>
102
- <input type="number" id="cost" name="cost" required>
103
- </div>
104
 
105
- <div class="form-group">
106
- <label for="receipt">發票 Receipt:</label>
107
- <input type="file" id="receipt" name="receipt" accept="image/*,.pdf" required>
108
- </div>
 
109
 
110
- <button type="submit">提交 Submit</button>
111
- </form>
112
- <div id="status"></div>
113
-
114
- <script>
115
- document.getElementById('purchaseForm').addEventListener('submit', async (e) => {
116
- e.preventDefault();
117
-
118
- const status = document.getElementById('status');
119
- status.style.display = 'block';
120
- status.textContent = '提交中... Submitting...';
121
- status.className = '';
122
-
123
- const formData = new FormData(e.target);
124
-
125
- try {
126
- const response = await fetch('/submit', {
127
- method: 'POST',
128
- body: formData
129
- });
130
-
131
- const result = await response.json();
132
-
133
- if (result.success) {
134
- status.className = 'success';
135
- status.textContent = '提交成功! ' + result.message;
136
- e.target.reset();
137
- } else {
138
- status.className = 'error';
139
- status.textContent = '錯誤:' + result.message;
140
- }
141
- } catch (error) {
142
- status.className = 'error';
143
- status.textContent = '提交錯誤 Error submitting form';
144
- }
145
- });
146
- </script>
147
- </body>
148
- </html>
149
- '''
150
 
151
- # Single route definition for index
152
  @app.route('/')
153
  def index():
 
 
154
  return render_template_string(HTML_TEMPLATE)
155
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  @app.route('/submit', methods=['POST'])
157
  def submit():
 
 
 
 
158
  try:
159
  name = request.form['name']
160
  student_id = request.form['studentId']
@@ -162,8 +235,6 @@ def submit():
162
  cost = request.form['cost']
163
  file = request.files['receipt']
164
 
165
- credentials = Credentials.from_authorized_user_info(CREDENTIALS)
166
-
167
  # Upload file to Drive
168
  drive_service = build('drive', 'v3', credentials=credentials)
169
 
@@ -208,4 +279,5 @@ def submit():
208
  return jsonify({'success': False, 'message': f'Error: {str(e)}'})
209
 
210
  if __name__ == '__main__':
 
211
  app.run(host='0.0.0.0', port=7860)
 
1
+ from flask import Flask, request, jsonify, render_template_string, redirect, session
2
+ from google_auth_oauthlib.flow import Flow
3
  from google.oauth2.credentials import Credentials
4
  from googleapiclient.discovery import build
5
  from googleapiclient.http import MediaIoBaseUpload
 
8
  import datetime
9
 
10
  app = Flask(__name__)
11
+ app.secret_key = os.environ.get('FLASK_SECRET_KEY')
12
 
13
  SPREADSHEET_ID = '1rcIJflbC1VIc70F6dnASLFvGQ90TXHhCx0iyxsXR7Ww'
14
  FOLDER_ID = '1brmLNqOMCvRS0TDY6ECHvIBmlRx8pcPz'
15
 
16
+ # OAuth 2.0 configuration
17
+ CLIENT_CONFIG = {
18
+ "web": {
19
+ "client_id": os.environ.get('GOOGLE_CLIENT_ID'),
20
+ "client_secret": os.environ.get('GOOGLE_CLIENT_SECRET'),
21
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
22
+ "token_uri": "https://oauth2.googleapis.com/token",
23
+ "redirect_uris": ["https://euler314-purchase-record.hf.space/oauth2callback"]
24
+ }
25
  }
26
 
27
+ SCOPES = [
28
+ 'https://www.googleapis.com/auth/spreadsheets',
29
+ 'https://www.googleapis.com/auth/drive.file'
30
+ ]
31
+
32
+ HTML_TEMPLATfrom flask import Flask, request, jsonify, render_template_string, redirect, session
33
+ from google_auth_oauthlib.flow import Flow
34
+ from google.oauth2.credentials import Credentials
35
+ from googleapiclient.discovery import build
36
+ from googleapiclient.http import MediaIoBaseUpload
37
+ import os
38
+ import io
39
+ import datetime
40
+
41
+ app = Flask(__name__)
42
+ app.secret_key = os.environ.get('FLASK_SECRET_KEY')
43
+
44
+ SPREADSHEET_ID = '1rcIJflbC1VIc70F6dnASLFvGQ90TXHhCx0iyxsXR7Ww'
45
+ FOLDER_ID = '1brmLNqOMCvRS0TDY6ECHvIBmlRx8pcPz'
46
+
47
+ # OAuth 2.0 configuration
48
+ CLIENT_CONFIG = {
49
+ "web": {
50
+ "client_id": os.environ.get('GOOGLE_CLIENT_ID'),
51
+ "client_secret": os.environ.get('GOOGLE_CLIENT_SECRET'),
52
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
53
+ "token_uri": "https://oauth2.googleapis.com/token",
54
+ "redirect_uris": ["https://euler314-purchase-record.hf.space/oauth2callback"]
55
+ }
56
+ }
57
+
58
+ SCOPES = [
59
+ 'https://www.googleapis.com/auth/spreadsheets',
60
+ 'https://www.googleapis.com/auth/drive.file'
61
+ ]
62
+
63
  HTML_TEMPLATE = '''
64
+ [Previous HTML template remains the same]
65
+ '''
66
+
67
+ @app.route('/')
68
+ def index():
69
+ if 'credentials' not in session:
70
+ return redirect('/login')
71
+ return render_template_string(HTML_TEMPLATE)
72
+
73
+ @app.route('/login')
74
+ def login():
75
+ flow = Flow.from_client_config(
76
+ CLIENT_CONFIG,
77
+ scopes=SCOPES,
78
+ redirect_uri=CLIENT_CONFIG['web']['redirect_uris'][0]
79
+ )
80
+ authorization_url, state = flow.authorization_url(
81
+ access_type='offline',
82
+ include_granted_scopes='true'
83
+ )
84
+ session['state'] = state
85
+ return redirect(authorization_url)
86
+
87
+ @app.route('/oauth2callback')
88
+ def oauth2callback():
89
+ flow = Flow.from_client_config(
90
+ CLIENT_CONFIG,
91
+ scopes=SCOPES,
92
+ state=session['state'],
93
+ redirect_uri=CLIENT_CONFIG['web']['redirect_uris'][0]
94
+ )
95
+
96
+ authorization_response = request.url
97
+ flow.fetch_token(authorization_response=authorization_response)
98
+
99
+ credentials = flow.credentials
100
+ session['credentials'] = {
101
+ 'token': credentials.token,
102
+ 'refresh_token': credentials.refresh_token,
103
+ 'token_uri': credentials.token_uri,
104
+ 'client_id': credentials.client_id,
105
+ 'client_secret': credentials.client_secret,
106
+ 'scopes': credentials.scopes
107
+ }
108
+
109
+ return redirect('/')
110
+
111
+ def get_credentials():
112
+ if 'credentials' not in session:
113
+ return None
114
+ return Credentials(**session['credentials'])
115
+
116
+ @app.route('/submit', methods=['POST'])
117
+ def submit():
118
+ credentials = get_credentials()
119
+ if not credentials:
120
+ return jsonify({'success': False, 'message': 'Authentication required'})
121
+
122
+ try:
123
+ name = request.form['name']
124
+ student_id = request.form['studentId']
125
+ product = request.form['product']
126
+ cost = request.form['cost']
127
+ file = request.files['receipt']
128
+
129
+ # Upload file to Drive
130
+ drive_service = build('drive', 'v3', credentials=credentials)
131
+
132
+ file_metadata = {
133
+ 'name': f"{student_id}_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}_{file.filename}",
134
+ 'parents': [FOLDER_ID]
135
  }
 
 
 
 
 
 
 
 
 
136
 
137
+ media = MediaIoBaseUpload(
138
+ io.BytesIO(file.read()),
139
+ mimetype=file.content_type,
140
+ resumable=True
141
+ )
142
 
143
+ uploaded_file = drive_service.files().create(
144
+ body=file_metadata,
145
+ media_body=media,
146
+ fields='id,webViewLink'
147
+ ).execute()
148
 
149
+ file_url = uploaded_file.get('webViewLink', '')
150
+
151
+ # Update Google Sheet
152
+ sheets_service = build('sheets', 'v4', credentials=credentials)
153
 
154
+ timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
155
+ ip_address = request.remote_addr
156
+
157
+ values = [[name, student_id, product, cost, file_url, timestamp, ip_address]]
158
+ body = {'values': values}
159
 
160
+ sheets_service.spreadsheets().values().append(
161
+ spreadsheetId=SPREADSHEET_ID,
162
+ range='Sheet1!A:G',
163
+ valueInputOption='USER_ENTERED',
164
+ body=body
165
+ ).execute()
166
+
167
+ return jsonify({'success': True, 'message': 'Data submitted successfully!'})
168
+
169
+ except Exception as e:
170
+ return jsonify({'success': False, 'message': f'Error: {str(e)}'})
171
+
172
+ if __name__ == '__main__':
173
+ os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
174
+ app.run(host='0.0.0.0', port=7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
 
 
176
  @app.route('/')
177
  def index():
178
+ if 'credentials' not in session:
179
+ return redirect('/login')
180
  return render_template_string(HTML_TEMPLATE)
181
 
182
+ @app.route('/login')
183
+ def login():
184
+ flow = Flow.from_client_config(
185
+ CLIENT_CONFIG,
186
+ scopes=SCOPES,
187
+ redirect_uri=CLIENT_CONFIG['web']['redirect_uris'][0]
188
+ )
189
+ authorization_url, state = flow.authorization_url(
190
+ access_type='offline',
191
+ include_granted_scopes='true'
192
+ )
193
+ session['state'] = state
194
+ return redirect(authorization_url)
195
+
196
+ @app.route('/oauth2callback')
197
+ def oauth2callback():
198
+ flow = Flow.from_client_config(
199
+ CLIENT_CONFIG,
200
+ scopes=SCOPES,
201
+ state=session['state'],
202
+ redirect_uri=CLIENT_CONFIG['web']['redirect_uris'][0]
203
+ )
204
+
205
+ authorization_response = request.url
206
+ flow.fetch_token(authorization_response=authorization_response)
207
+
208
+ credentials = flow.credentials
209
+ session['credentials'] = {
210
+ 'token': credentials.token,
211
+ 'refresh_token': credentials.refresh_token,
212
+ 'token_uri': credentials.token_uri,
213
+ 'client_id': credentials.client_id,
214
+ 'client_secret': credentials.client_secret,
215
+ 'scopes': credentials.scopes
216
+ }
217
+
218
+ return redirect('/')
219
+
220
+ def get_credentials():
221
+ if 'credentials' not in session:
222
+ return None
223
+ return Credentials(**session['credentials'])
224
+
225
  @app.route('/submit', methods=['POST'])
226
  def submit():
227
+ credentials = get_credentials()
228
+ if not credentials:
229
+ return jsonify({'success': False, 'message': 'Authentication required'})
230
+
231
  try:
232
  name = request.form['name']
233
  student_id = request.form['studentId']
 
235
  cost = request.form['cost']
236
  file = request.files['receipt']
237
 
 
 
238
  # Upload file to Drive
239
  drive_service = build('drive', 'v3', credentials=credentials)
240
 
 
279
  return jsonify({'success': False, 'message': f'Error: {str(e)}'})
280
 
281
  if __name__ == '__main__':
282
+ os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
283
  app.run(host='0.0.0.0', port=7860)