euler314 commited on
Commit
57fc3fb
·
verified ·
1 Parent(s): 55b202d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +121 -31
app.py CHANGED
@@ -22,25 +22,32 @@ if CREDS_JSON:
22
  'https://www.googleapis.com/auth/drive.file']
23
  )
24
 
25
- def ensure_headers():
 
 
 
 
 
 
 
 
 
26
  try:
27
  sheets_service = build('sheets', 'v4', credentials=CREDENTIALS)
28
 
29
- # Directly update first row with headers
30
  headers = [
31
  ['姓名 Name', '學號 Student ID', '購買商品 Product',
32
  '金額 Cost', '發票檔案 Receipt File', '時間 Timestamp', 'IP位址 IP Address']
33
  ]
34
 
35
- # Update A1:G1 with headers
36
  sheets_service.spreadsheets().values().update(
37
  spreadsheetId=SPREADSHEET_ID,
38
- range='Sheet1!A1:G1',
39
  valueInputOption='RAW',
40
  body={'values': headers}
41
  ).execute()
42
 
43
- # Format headers to be bold and centered
44
  requests = [{
45
  'repeatCell': {
46
  'range': {
@@ -67,9 +74,6 @@ def ensure_headers():
67
 
68
  except Exception as e:
69
  print(f"Error setting headers: {str(e)}")
70
- @app.before_first_request
71
- def initialize():
72
- ensure_headers()
73
 
74
  HTML_TEMPLATE = '''
75
  <!DOCTYPE html>
@@ -80,7 +84,7 @@ HTML_TEMPLATE = '''
80
  <style>
81
  body {
82
  font-family: Arial, sans-serif;
83
- max-width: 600px;
84
  margin: 20px auto;
85
  padding: 20px;
86
  }
@@ -92,12 +96,13 @@ HTML_TEMPLATE = '''
92
  margin-bottom: 8px;
93
  font-weight: bold;
94
  }
95
- input {
96
  width: 100%;
97
  padding: 10px;
98
  border: 1px solid #ddd;
99
  border-radius: 4px;
100
  font-size: 16px;
 
101
  }
102
  button {
103
  background-color: #4CAF50;
@@ -107,7 +112,6 @@ HTML_TEMPLATE = '''
107
  border-radius: 4px;
108
  cursor: pointer;
109
  font-size: 16px;
110
- width: 100%;
111
  }
112
  button:hover {
113
  background-color: #45a049;
@@ -126,44 +130,118 @@ HTML_TEMPLATE = '''
126
  background-color: #f2dede;
127
  color: #a94442;
128
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  </style>
130
  </head>
131
  <body>
132
  <h2>購買記錄表單 Purchase Record Form</h2>
133
  <form id="purchaseForm">
134
  <div class="form-group">
135
- <label for="name">姓名 Name:</label>
136
- <input type="text" id="name" name="name" required>
 
 
 
 
137
  </div>
138
-
139
- <div class="form-group">
140
- <label for="studentId">學號 Student ID:</label>
141
- <input type="text" id="studentId" name="studentId" required>
142
- </div>
143
-
144
  <div class="form-group">
145
  <label for="product">購買商品 Product:</label>
146
  <input type="text" id="product" name="product" required>
147
  </div>
148
 
149
- <div class="form-group">
150
- <label for="cost">金額 Cost:</label>
151
- <input type="number" id="cost" name="cost" required>
152
- </div>
153
-
154
  <div class="form-group">
155
  <label for="receipt">發票 Receipt:</label>
156
  <input type="file" id="receipt" name="receipt" accept="image/*,.pdf" required>
157
  </div>
 
 
 
 
 
 
 
 
 
 
158
 
159
  <button type="submit">提交 Submit</button>
160
  </form>
161
  <div id="status"></div>
162
 
163
  <script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  document.getElementById('purchaseForm').addEventListener('submit', async (e) => {
165
  e.preventDefault();
166
 
 
 
 
 
 
167
  const status = document.getElementById('status');
168
  status.style.display = 'block';
169
  status.textContent = '提交中... Submitting...';
@@ -183,6 +261,9 @@ HTML_TEMPLATE = '''
183
  status.className = 'success';
184
  status.textContent = '提交成功! ' + result.message;
185
  e.target.reset();
 
 
 
186
  } else {
187
  status.className = 'error';
188
  status.textContent = '錯誤:' + result.message;
@@ -199,22 +280,24 @@ HTML_TEMPLATE = '''
199
 
200
  @app.route('/')
201
  def index():
202
- return render_template_string(HTML_TEMPLATE)
 
203
 
204
  @app.route('/submit', methods=['POST'])
205
  def submit():
206
  try:
207
- name = request.form['name']
208
- student_id = request.form['studentId']
209
  product = request.form['product']
210
- cost = request.form['cost']
211
  file = request.files['receipt']
 
 
 
212
 
213
  # Upload file to Drive
214
  drive_service = build('drive', 'v3', credentials=CREDENTIALS)
215
 
216
  file_metadata = {
217
- 'name': f"{student_id}_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}_{file.filename}",
218
  'parents': [FOLDER_ID]
219
  }
220
 
@@ -232,18 +315,25 @@ def submit():
232
 
233
  file_url = uploaded_file.get('webViewLink', '')
234
 
 
 
 
235
  # Update Google Sheet
236
  sheets_service = build('sheets', 'v4', credentials=CREDENTIALS)
237
 
238
  timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
239
  ip_address = request.remote_addr
240
 
241
- values = [[name, student_id, product, cost, file_url, timestamp, ip_address]]
 
 
 
 
242
  body = {'values': values}
243
 
244
  sheets_service.spreadsheets().values().append(
245
  spreadsheetId=SPREADSHEET_ID,
246
- range='Sheet1!A:G',
247
  valueInputOption='USER_ENTERED',
248
  body=body
249
  ).execute()
 
22
  'https://www.googleapis.com/auth/drive.file']
23
  )
24
 
25
+ def get_sheet_names():
26
+ try:
27
+ service = build('sheets', 'v4', credentials=CREDENTIALS)
28
+ spreadsheet = service.spreadsheets().get(spreadsheetId=SPREADSHEET_ID).execute()
29
+ return [sheet['properties']['title'] for sheet in spreadsheet['sheets']]
30
+ except Exception as e:
31
+ print(f"Error getting sheet names: {str(e)}")
32
+ return ['Sheet1']
33
+
34
+ def ensure_headers(sheet_name):
35
  try:
36
  sheets_service = build('sheets', 'v4', credentials=CREDENTIALS)
37
 
 
38
  headers = [
39
  ['姓名 Name', '學號 Student ID', '購買商品 Product',
40
  '金額 Cost', '發票檔案 Receipt File', '時間 Timestamp', 'IP位址 IP Address']
41
  ]
42
 
 
43
  sheets_service.spreadsheets().values().update(
44
  spreadsheetId=SPREADSHEET_ID,
45
+ range=f'{sheet_name}!A1:G1',
46
  valueInputOption='RAW',
47
  body={'values': headers}
48
  ).execute()
49
 
50
+ # Format headers
51
  requests = [{
52
  'repeatCell': {
53
  'range': {
 
74
 
75
  except Exception as e:
76
  print(f"Error setting headers: {str(e)}")
 
 
 
77
 
78
  HTML_TEMPLATE = '''
79
  <!DOCTYPE html>
 
84
  <style>
85
  body {
86
  font-family: Arial, sans-serif;
87
+ max-width: 800px;
88
  margin: 20px auto;
89
  padding: 20px;
90
  }
 
96
  margin-bottom: 8px;
97
  font-weight: bold;
98
  }
99
+ input, select {
100
  width: 100%;
101
  padding: 10px;
102
  border: 1px solid #ddd;
103
  border-radius: 4px;
104
  font-size: 16px;
105
+ margin-bottom: 10px;
106
  }
107
  button {
108
  background-color: #4CAF50;
 
112
  border-radius: 4px;
113
  cursor: pointer;
114
  font-size: 16px;
 
115
  }
116
  button:hover {
117
  background-color: #45a049;
 
130
  background-color: #f2dede;
131
  color: #a94442;
132
  }
133
+ .person-entry {
134
+ border: 1px solid #ddd;
135
+ padding: 15px;
136
+ margin-bottom: 15px;
137
+ border-radius: 4px;
138
+ }
139
+ .remove-person {
140
+ background-color: #dc3545;
141
+ margin-top: 10px;
142
+ }
143
+ #add-person {
144
+ margin-bottom: 20px;
145
+ background-color: #17a2b8;
146
+ }
147
+ .total-cost {
148
+ font-size: 1.2em;
149
+ font-weight: bold;
150
+ margin: 20px 0;
151
+ }
152
  </style>
153
  </head>
154
  <body>
155
  <h2>購買記錄表單 Purchase Record Form</h2>
156
  <form id="purchaseForm">
157
  <div class="form-group">
158
+ <label for="sheetSelect">選擇工作表 Select Sheet:</label>
159
+ <select id="sheetSelect" name="sheetName" required>
160
+ {% for sheet in sheet_names %}
161
+ <option value="{{ sheet }}">{{ sheet }}</option>
162
+ {% endfor %}
163
+ </select>
164
  </div>
165
+
 
 
 
 
 
166
  <div class="form-group">
167
  <label for="product">購買商品 Product:</label>
168
  <input type="text" id="product" name="product" required>
169
  </div>
170
 
 
 
 
 
 
171
  <div class="form-group">
172
  <label for="receipt">發票 Receipt:</label>
173
  <input type="file" id="receipt" name="receipt" accept="image/*,.pdf" required>
174
  </div>
175
+
176
+ <div id="people-container">
177
+ <!-- Person entries will be added here -->
178
+ </div>
179
+
180
+ <button type="button" id="add-person">新增人員 Add Person</button>
181
+
182
+ <div class="total-cost">
183
+ 總金額 Total Cost: <span id="total">0</span>
184
+ </div>
185
 
186
  <button type="submit">提交 Submit</button>
187
  </form>
188
  <div id="status"></div>
189
 
190
  <script>
191
+ let personCounter = 0;
192
+
193
+ function createPersonEntry() {
194
+ const container = document.createElement('div');
195
+ container.className = 'person-entry';
196
+
197
+ container.innerHTML = `
198
+ <div class="form-group">
199
+ <label>姓名 Name:</label>
200
+ <input type="text" name="names[]" required>
201
+ </div>
202
+ <div class="form-group">
203
+ <label>學號 Student ID:</label>
204
+ <input type="text" name="studentIds[]" required>
205
+ </div>
206
+ <div class="form-group">
207
+ <label>金額 Cost:</label>
208
+ <input type="number" name="costs[]" required onchange="updateTotal()">
209
+ </div>
210
+ <button type="button" class="remove-person" onclick="removePerson(this)">移除 Remove</button>
211
+ `;
212
+
213
+ return container;
214
+ }
215
+
216
+ function updateTotal() {
217
+ const costs = Array.from(document.getElementsByName('costs[]'))
218
+ .map(input => Number(input.value) || 0);
219
+ const total = costs.reduce((sum, cost) => sum + cost, 0);
220
+ document.getElementById('total').textContent = total;
221
+ }
222
+
223
+ function removePerson(button) {
224
+ button.parentElement.remove();
225
+ updateTotal();
226
+ }
227
+
228
+ document.getElementById('add-person').addEventListener('click', () => {
229
+ const container = document.getElementById('people-container');
230
+ container.appendChild(createPersonEntry());
231
+ personCounter++;
232
+ });
233
+
234
+ // Add initial person entry
235
+ document.getElementById('add-person').click();
236
+
237
  document.getElementById('purchaseForm').addEventListener('submit', async (e) => {
238
  e.preventDefault();
239
 
240
+ if (document.getElementsByName('names[]').length === 0) {
241
+ alert('請至少新增一個人員 Please add at least one person');
242
+ return;
243
+ }
244
+
245
  const status = document.getElementById('status');
246
  status.style.display = 'block';
247
  status.textContent = '提交中... Submitting...';
 
261
  status.className = 'success';
262
  status.textContent = '提交成功! ' + result.message;
263
  e.target.reset();
264
+ document.getElementById('people-container').innerHTML = '';
265
+ document.getElementById('add-person').click();
266
+ document.getElementById('total').textContent = '0';
267
  } else {
268
  status.className = 'error';
269
  status.textContent = '錯誤:' + result.message;
 
280
 
281
  @app.route('/')
282
  def index():
283
+ sheet_names = get_sheet_names()
284
+ return render_template_string(HTML_TEMPLATE, sheet_names=sheet_names)
285
 
286
  @app.route('/submit', methods=['POST'])
287
  def submit():
288
  try:
289
+ sheet_name = request.form['sheetName']
 
290
  product = request.form['product']
 
291
  file = request.files['receipt']
292
+ names = request.form.getlist('names[]')
293
+ student_ids = request.form.getlist('studentIds[]')
294
+ costs = request.form.getlist('costs[]')
295
 
296
  # Upload file to Drive
297
  drive_service = build('drive', 'v3', credentials=CREDENTIALS)
298
 
299
  file_metadata = {
300
+ 'name': f"{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}_{file.filename}",
301
  'parents': [FOLDER_ID]
302
  }
303
 
 
315
 
316
  file_url = uploaded_file.get('webViewLink', '')
317
 
318
+ # Ensure headers exist
319
+ ensure_headers(sheet_name)
320
+
321
  # Update Google Sheet
322
  sheets_service = build('sheets', 'v4', credentials=CREDENTIALS)
323
 
324
  timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
325
  ip_address = request.remote_addr
326
 
327
+ # Create entries for each person
328
+ values = []
329
+ for name, student_id, cost in zip(names, student_ids, costs):
330
+ values.append([name, student_id, product, cost, file_url, timestamp, ip_address])
331
+
332
  body = {'values': values}
333
 
334
  sheets_service.spreadsheets().values().append(
335
  spreadsheetId=SPREADSHEET_ID,
336
+ range=f'{sheet_name}!A:G',
337
  valueInputOption='USER_ENTERED',
338
  body=body
339
  ).execute()