euler314 commited on
Commit
7af6ed1
·
verified ·
1 Parent(s): 5f32809

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +137 -187
app.py CHANGED
@@ -1,6 +1,5 @@
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
6
  import os
@@ -8,207 +7,158 @@ import io
8
  import datetime
9
 
10
  app = Flask(__name__)
11
- app.secret_key = os.environ.get('FLASK_SECRET_KEY')
12
-
13
- SCOPES = ['https://www.googleapis.com/auth/spreadsheets',
14
- 'https://www.googleapis.com/auth/drive.file']
15
 
 
 
16
  SPREADSHEET_ID = '1rcIJflbC1VIc70F6dnASLFvGQ90TXHhCx0iyxsXR7Ww'
17
  FOLDER_ID = '1brmLNqOMCvRS0TDY6ECHvIBmlRx8pcPz'
18
 
19
- # OAuth configuration
20
- client_config = {
21
- "web": {
22
- "client_id": os.environ.get('GOOGLE_CLIENT_ID'),
23
- "client_secret": os.environ.get('GOOGLE_CLIENT_SECRET'),
24
- "auth_uri": "https://accounts.google.com/o/oauth2/auth",
25
- "token_uri": "https://oauth2.googleapis.com/token",
26
- "redirect_uris": ["https://euler314-purchase-record.hf.space/oauth2callback"]
27
- }
 
 
 
28
  }
29
 
30
- HTML_TEMPLATE = '''
31
- <!DOCTYPE html>
32
- <html>
33
- <head>
34
- <title>Purchase Record Form</title>
35
- <meta charset="UTF-8">
36
- <style>
37
- body {
38
- font-family: Arial, sans-serif;
39
- max-width: 600px;
40
- margin: 20px auto;
41
- padding: 20px;
42
- }
43
- .form-group {
44
- margin-bottom: 20px;
45
- }
46
- label {
47
- display: block;
48
- margin-bottom: 8px;
49
- font-weight: bold;
50
- }
51
- input {
52
- width: 100%;
53
- padding: 10px;
54
- border: 1px solid #ddd;
55
- border-radius: 4px;
56
- font-size: 16px;
57
- }
58
- button {
59
- background-color: #4CAF50;
60
- color: white;
61
- padding: 12px 24px;
62
- border: none;
63
- border-radius: 4px;
64
- cursor: pointer;
65
- font-size: 16px;
66
- width: 100%;
67
- }
68
- button:hover {
69
- background-color: #45a049;
70
- }
71
- #status {
72
- margin-top: 20px;
73
- padding: 15px;
74
- border-radius: 4px;
75
- display: none;
76
- }
77
- .success {
78
- background-color: #dff0d8;
79
- color: #3c763d;
80
- }
81
- .error {
82
- background-color: #f2dede;
83
- color: #a94442;
84
- }
85
- </style>
86
- </head>
87
- <body>
88
- <h2>購買記錄表單 Purchase Record Form</h2>
89
- <form id="purchaseForm">
90
- <div class="form-group">
91
- <label for="name">姓名 Name:</label>
92
- <input type="text" id="name" name="name" required>
93
- </div>
94
-
95
- <div class="form-group">
96
- <label for="studentId">學號 Student ID:</label>
97
- <input type="text" id="studentId" name="studentId" required>
98
- </div>
99
-
100
- <div class="form-group">
101
- <label for="product">購買商品 Product:</label>
102
- <input type="text" id="product" name="product" required>
103
- </div>
104
-
105
- <div class="form-group">
106
- <label for="cost">金額 Cost:</label>
107
- <input type="number" id="cost" name="cost" required>
108
- </div>
109
-
110
- <div class="form-group">
111
- <label for="receipt">發票 Receipt:</label>
112
- <input type="file" id="receipt" name="receipt" accept="image/*,.pdf" required>
113
- </div>
114
-
115
- <button type="submit">提交 Submit</button>
116
- </form>
117
- <div id="status"></div>
118
 
119
- <script>
120
- document.getElementById('purchaseForm').addEventListener('submit', async (e) => {
121
- e.preventDefault();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
- const status = document.getElementById('status');
124
- status.style.display = 'block';
125
- status.textContent = '提交中... Submitting...';
126
- status.className = '';
127
 
128
- const formData = new FormData(e.target);
 
 
 
129
 
130
- try {
131
- const response = await fetch('/submit', {
132
- method: 'POST',
133
- body: formData
134
- });
 
 
 
 
 
 
 
135
 
136
- const result = await response.json();
 
 
 
137
 
138
- if (result.success) {
139
- status.className = 'success';
140
- status.textContent = '提交成功! ' + result.message;
141
- e.target.reset();
142
- } else {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  status.className = 'error';
144
- status.textContent = '錯誤:' + result.message;
145
  }
146
- } catch (error) {
147
- status.className = 'error';
148
- status.textContent = '提交錯誤 Error submitting form';
149
- }
150
- });
151
- </script>
152
- </body>
153
- </html>
154
- '''
155
-
156
- def credentials_to_dict(credentials):
157
- return {
158
- 'token': credentials.token,
159
- 'refresh_token': credentials.refresh_token,
160
- 'token_uri': credentials.token_uri,
161
- 'client_id': credentials.client_id,
162
- 'client_secret': credentials.client_secret,
163
- 'scopes': credentials.scopes
164
- }
165
-
166
- def get_credentials():
167
- if 'credentials' not in session:
168
- return None
169
- return Credentials(**session['credentials'])
170
-
171
- @app.route('/authorize')
172
- def authorize():
173
- flow = Flow.from_client_config(
174
- client_config,
175
- scopes=SCOPES,
176
- redirect_uri=client_config['web']['redirect_uris'][0]
177
- )
178
- authorization_url, state = flow.authorization_url(
179
- access_type='offline',
180
- include_granted_scopes='true'
181
- )
182
- session['state'] = state
183
- return redirect(authorization_url)
184
-
185
- @app.route('/oauth2callback')
186
- def oauth2callback():
187
- state = session['state']
188
- flow = Flow.from_client_config(
189
- client_config,
190
- scopes=SCOPES,
191
- state=state,
192
- redirect_uri=client_config['web']['redirect_uris'][0]
193
- )
194
- authorization_response = request.url
195
- flow.fetch_token(authorization_response=authorization_response)
196
- credentials = flow.credentials
197
- session['credentials'] = credentials_to_dict(credentials)
198
- return redirect('/')
199
-
200
- @app.route('/')
201
- def home(): # Changed from 'index' to 'home'
202
- if not get_credentials():
203
- return redirect('/authorize')
204
- return render_template_string(HTML_TEMPLATE)
205
 
206
  @app.route('/submit', methods=['POST'])
207
  def submit():
208
- credentials = get_credentials()
209
- if not credentials:
210
- return jsonify({'success': False, 'message': 'Authentication required'})
211
-
212
  try:
213
  name = request.form['name']
214
  student_id = request.form['studentId']
 
1
+ from flask import Flask, request, jsonify, render_template_string
2
+ from google.oauth2.service_account import Credentials
 
3
  from googleapiclient.discovery import build
4
  from googleapiclient.http import MediaIoBaseUpload
5
  import os
 
7
  import datetime
8
 
9
  app = Flask(__name__)
 
 
 
 
10
 
11
+ # Google API settings
12
+ SCOPES = ['https://www.googleapis.com/auth/spreadsheets', 'https://www.googleapis.com/auth/drive.file']
13
  SPREADSHEET_ID = '1rcIJflbC1VIc70F6dnASLFvGQ90TXHhCx0iyxsXR7Ww'
14
  FOLDER_ID = '1brmLNqOMCvRS0TDY6ECHvIBmlRx8pcPz'
15
 
16
+ # Create credentials from environment variables
17
+ creds_dict = {
18
+ "type": "service_account",
19
+ "project_id": os.environ.get('PROJECT_ID'),
20
+ "private_key_id": os.environ.get('PRIVATE_KEY_ID'),
21
+ "private_key": os.environ.get('PRIVATE_KEY').replace('\\n', '\n'),
22
+ "client_email": os.environ.get('CLIENT_EMAIL'),
23
+ "client_id": os.environ.get('CLIENT_ID'),
24
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
25
+ "token_uri": "https://oauth2.googleapis.com/token",
26
+ "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
27
+ "client_x509_cert_url": os.environ.get('CLIENT_X509_CERT_URL')
28
  }
29
 
30
+ credentials = Credentials.from_service_account_info(creds_dict, scopes=SCOPES)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
+ @app.route('/')
33
+ def home():
34
+ return render_template_string('''
35
+ <!DOCTYPE html>
36
+ <html>
37
+ <head>
38
+ <title>Purchase Record Form</title>
39
+ <meta charset="UTF-8">
40
+ <style>
41
+ body {
42
+ font-family: Arial, sans-serif;
43
+ max-width: 600px;
44
+ margin: 20px auto;
45
+ padding: 20px;
46
+ }
47
+ .form-group {
48
+ margin-bottom: 20px;
49
+ }
50
+ label {
51
+ display: block;
52
+ margin-bottom: 8px;
53
+ font-weight: bold;
54
+ }
55
+ input {
56
+ width: 100%;
57
+ padding: 10px;
58
+ border: 1px solid #ddd;
59
+ border-radius: 4px;
60
+ font-size: 16px;
61
+ }
62
+ button {
63
+ background-color: #4CAF50;
64
+ color: white;
65
+ padding: 12px 24px;
66
+ border: none;
67
+ border-radius: 4px;
68
+ cursor: pointer;
69
+ font-size: 16px;
70
+ width: 100%;
71
+ }
72
+ button:hover {
73
+ background-color: #45a049;
74
+ }
75
+ #status {
76
+ margin-top: 20px;
77
+ padding: 15px;
78
+ border-radius: 4px;
79
+ display: none;
80
+ }
81
+ .success {
82
+ background-color: #dff0d8;
83
+ color: #3c763d;
84
+ }
85
+ .error {
86
+ background-color: #f2dede;
87
+ color: #a94442;
88
+ }
89
+ </style>
90
+ </head>
91
+ <body>
92
+ <h2>購買記錄表單 Purchase Record Form</h2>
93
+ <form id="purchaseForm">
94
+ <div class="form-group">
95
+ <label for="name">姓名 Name:</label>
96
+ <input type="text" id="name" name="name" required>
97
+ </div>
98
+
99
+ <div class="form-group">
100
+ <label for="studentId">學號 Student ID:</label>
101
+ <input type="text" id="studentId" name="studentId" required>
102
+ </div>
103
 
104
+ <div class="form-group">
105
+ <label for="product">購買商品 Product:</label>
106
+ <input type="text" id="product" name="product" required>
107
+ </div>
108
 
109
+ <div class="form-group">
110
+ <label for="cost">金額 Cost:</label>
111
+ <input type="number" id="cost" name="cost" required>
112
+ </div>
113
 
114
+ <div class="form-group">
115
+ <label for="receipt">發票 Receipt:</label>
116
+ <input type="file" id="receipt" name="receipt" accept="image/*,.pdf" required>
117
+ </div>
118
+
119
+ <button type="submit">提交 Submit</button>
120
+ </form>
121
+ <div id="status"></div>
122
+
123
+ <script>
124
+ document.getElementById('purchaseForm').addEventListener('submit', async (e) => {
125
+ e.preventDefault();
126
 
127
+ const status = document.getElementById('status');
128
+ status.style.display = 'block';
129
+ status.textContent = '提交中... Submitting...';
130
+ status.className = '';
131
 
132
+ const formData = new FormData(e.target);
133
+
134
+ try {
135
+ const response = await fetch('/submit', {
136
+ method: 'POST',
137
+ body: formData
138
+ });
139
+
140
+ const result = await response.json();
141
+
142
+ if (result.success) {
143
+ status.className = 'success';
144
+ status.textContent = '提交成功! ' + result.message;
145
+ e.target.reset();
146
+ } else {
147
+ status.className = 'error';
148
+ status.textContent = '錯誤:' + result.message;
149
+ }
150
+ } catch (error) {
151
  status.className = 'error';
152
+ status.textContent = '提交錯誤 Error submitting form';
153
  }
154
+ });
155
+ </script>
156
+ </body>
157
+ </html>
158
+ ''')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
 
160
  @app.route('/submit', methods=['POST'])
161
  def submit():
 
 
 
 
162
  try:
163
  name = request.form['name']
164
  student_id = request.form['studentId']