mohamedtsou commited on
Commit
65a4124
ยท
1 Parent(s): 05ffb1c

use Firebase Firestore instead of SQLite

Browse files
Files changed (2) hide show
  1. app.py +89 -76
  2. requirements.txt +1 -1
app.py CHANGED
@@ -1,9 +1,7 @@
1
- from flask import Flask, render_template, request, jsonify, send_from_directory, make_response
2
- from flask_sqlalchemy import SQLAlchemy
3
- from werkzeug.utils import secure_filename
4
- from reportlab.lib.pagesizes import A4, landscape
5
- from reportlab.lib.units import cm, mm
6
- from reportlab.lib.colors import HexColor, white, black, Color
7
  from reportlab.pdfgen import canvas
8
  from reportlab.pdfbase import pdfmetrics
9
  from reportlab.pdfbase.ttfonts import TTFont
@@ -11,10 +9,18 @@ from PIL import Image as PILImage
11
  import arabic_reshaper
12
  from bidi.algorithm import get_display
13
  import io
14
- import os
15
  import base64
16
- import tempfile
17
- from datetime import datetime
 
 
 
 
 
 
 
 
 
18
 
19
  pdfmetrics.registerFont(TTFont('Arabic', 'fonts/NotoNaskhArabic-Regular.ttf'))
20
  pdfmetrics.registerFont(TTFont('ArabicBold', 'fonts/NotoNaskhArabic-Bold.ttf'))
@@ -26,83 +32,93 @@ def reshape_arabic(text):
26
  return get_display(reshaped)
27
 
28
  app = Flask(__name__)
29
- app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///badges.db'
30
- app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
31
- app.config['UPLOAD_FOLDER'] = 'uploads'
32
- app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
33
 
34
- os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
35
-
36
- db = SQLAlchemy(app)
37
-
38
- class Badge(db.Model):
39
- id = db.Column(db.Integer, primary_key=True)
40
- first_name = db.Column(db.String(100), nullable=False)
41
- last_name = db.Column(db.String(100), nullable=False)
42
- mission = db.Column(db.String(200))
43
- photo_data = db.Column(db.Text)
44
- bg_image_data = db.Column(db.Text)
45
- background_color = db.Column(db.String(200), default='linear-gradient(135deg,#2d4a1e,#556b2f)')
46
- created_at = db.Column(db.DateTime, default=datetime.utcnow)
 
 
 
 
 
 
 
 
 
47
 
48
- def to_dict(self):
49
- return {
50
- 'id': self.id,
51
- 'firstName': self.first_name,
52
- 'lastName': self.last_name,
53
- 'name': f'{self.first_name} {self.last_name}',
54
- 'mission': self.mission,
55
- 'photoUrl': self.photo_data or 'https://via.placeholder.com/150',
56
- 'bgImage': self.bg_image_data,
57
- 'backgroundColor': self.background_color,
58
- 'createdAt': int(self.created_at.timestamp() * 1000) if self.created_at else 0
59
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
  @app.route('/')
62
  def index():
63
  return render_template('badge.html')
64
 
65
  @app.route('/api/badges', methods=['GET'])
66
- def get_badges():
67
- badges = Badge.query.order_by(Badge.created_at.desc()).all()
68
- return jsonify([b.to_dict() for b in badges])
69
 
70
  @app.route('/api/badges', methods=['POST'])
71
- def create_badge():
72
  data = request.get_json()
73
-
74
  if not data or not data.get('firstName') or not data.get('lastName'):
75
  return jsonify({'error': 'First name and last name are required'}), 400
76
 
77
- badge = Badge(
78
- first_name=data['firstName'],
79
- last_name=data['lastName'],
80
- mission=data.get('mission', ''),
81
- photo_data=data.get('photoUrl'),
82
- bg_image_data=data.get('bgImage'),
83
- background_color=data.get('backgroundColor', 'linear-gradient(135deg,#2d4a1e,#556b2f)')
84
- )
85
-
86
- db.session.add(badge)
87
- db.session.commit()
88
-
89
- return jsonify(badge.to_dict()), 201
90
 
91
- @app.route('/api/badges/<int:badge_id>', methods=['DELETE'])
92
- def delete_badge(badge_id):
93
- badge = Badge.query.get_or_404(badge_id)
94
- db.session.delete(badge)
95
- db.session.commit()
96
- return jsonify({'message': 'Badge deleted'})
97
 
98
  @app.route('/api/export/print')
99
  def export_print():
100
- badges = Badge.query.order_by(Badge.created_at.desc()).all()
101
 
102
  badges_html = ""
103
  for badge in badges:
104
- bg_style = f"background-image: url({badge.bg_image_data}); background-size: cover; background-position: center;" if badge.bg_image_data else f"background: {badge.background_color};"
105
- photo = badge.photo_data if badge.photo_data else 'https://via.placeholder.com/150'
106
 
107
  badges_html += f"""
108
  <div class="badge-item">
@@ -112,11 +128,11 @@ def export_print():
112
  <div class="badge-content">
113
  <div class="badge-scout-icon">โšœ๏ธ</div>
114
  <img class="badge-photo" src="{photo}" alt="">
115
- <div class="badge-name">{badge.first_name} {badge.last_name}</div>
116
  </div>
117
  <div class="badge-mission-wrap">
118
  <div class="badge-mission-label">ุงู„ู…ู‡ู…ุฉ</div>
119
- <div class="badge-mission-value">{badge.mission or 'โ€”'}</div>
120
  </div>
121
  </div>
122
  </div>
@@ -171,7 +187,7 @@ body {{ font-family: 'Tajawal', sans-serif; background: white; padding: 20px; }}
171
 
172
  @app.route('/api/export/pdf')
173
  def export_pdf():
174
- badges = Badge.query.order_by(Badge.created_at.desc()).all()
175
 
176
  badge_width = 8.5 * cm
177
  badge_height = 12 * cm
@@ -217,7 +233,7 @@ def export_pdf():
217
  c.setLineWidth(3)
218
  c.roundRect(2, 2, badge_width - 4, badge_height - 4, 6, fill=0, stroke=1)
219
 
220
- bg_buf = get_image_buffer(badge.bg_image_data, size=(int(badge_width/cm*100), int(badge_height/cm*100)))
221
  if bg_buf:
222
  c.drawImage(bg_buf, 0, 0, width=badge_width, height=badge_height, preserveAspectRatio=False, mask='auto')
223
  c.setFillColor(HexColor('#000000'))
@@ -241,7 +257,7 @@ def export_pdf():
241
  c.drawImage(logo_buf, badge_width/2 - 0.7*cm, center_y + 1.5*cm, width=1.4*cm, height=1.4*cm)
242
 
243
  photo_size = 2.2 * cm
244
- photo_buf = get_image_buffer(badge.photo_data, size=(200, 200))
245
  if photo_buf:
246
  c.saveState()
247
  c.beginPath()
@@ -259,7 +275,7 @@ def export_pdf():
259
  c.setFont('ArabicBold', 24)
260
  c.drawCentredString(badge_width/2, center_y - 0.3*cm, "๐Ÿ“ท")
261
 
262
- name = f"{badge.first_name} {badge.last_name}"
263
  c.setFillColor(HexColor('#ffffff'))
264
  c.setFont('ArabicBold', 12)
265
  c.setFillColor(HexColor('#000000'))
@@ -272,7 +288,9 @@ def export_pdf():
272
  c.setFillColor(HexColor('#3d3010'))
273
  c.rect(0, 0, badge_width, 1.3*cm, fill=1, stroke=0)
274
  draw_text_rtl(c, "ุงู„ู…ู‡ู…ุฉ", badge_width/2, 0.9*cm, 'Arabic', 9, HexColor('#c8a96e'))
275
- mission_text = badge.mission[:20] if badge.mission and len(badge.mission) > 20 else (badge.mission or "โ€”")
 
 
276
  draw_text_rtl(c, mission_text, badge_width/2, 0.35*cm, 'ArabicBold', 10, HexColor('#f5e6b0'))
277
 
278
  c.restoreState()
@@ -301,10 +319,5 @@ def export_pdf():
301
 
302
  return response
303
 
304
- def init_db():
305
- with app.app_context():
306
- db.create_all()
307
-
308
  if __name__ == '__main__':
309
- init_db()
310
  app.run(debug=True, host='0.0.0.0', port=5000)
 
1
+ from flask import Flask, render_template, request, jsonify, make_response
2
+ from reportlab.lib.pagesizes import A4
3
+ from reportlab.lib.units import cm
4
+ from reportlab.lib.colors import HexColor
 
 
5
  from reportlab.pdfgen import canvas
6
  from reportlab.pdfbase import pdfmetrics
7
  from reportlab.pdfbase.ttfonts import TTFont
 
9
  import arabic_reshaper
10
  from bidi.algorithm import get_display
11
  import io
 
12
  import base64
13
+ import requests
14
+ import os
15
+
16
+ FIREBASE_CONFIG = {
17
+ "apiKey": "AIzaSyAVacEYZNH0jIimwurEE0g4mTRUz1RvkT0",
18
+ "projectId": "waste-sorting-ai-36421",
19
+ "authDomain": "waste-sorting-ai-36421.firebaseapp.com",
20
+ "storageBucket": "waste-sorting-ai-36421.firebasestorage.app"
21
+ }
22
+
23
+ FIREBASE_URL = f"https://firestore.googleapis.com/v1/projects/{FIREBASE_CONFIG['projectId']}/databases/(default)/documents"
24
 
25
  pdfmetrics.registerFont(TTFont('Arabic', 'fonts/NotoNaskhArabic-Regular.ttf'))
26
  pdfmetrics.registerFont(TTFont('ArabicBold', 'fonts/NotoNaskhArabic-Bold.ttf'))
 
32
  return get_display(reshaped)
33
 
34
  app = Flask(__name__)
 
 
 
 
35
 
36
+ def get_badges():
37
+ try:
38
+ resp = requests.get(f"{FIREBASE_URL}/badges", params={"orderBy": '"createdAt desc"'})
39
+ resp.raise_for_status()
40
+ docs = resp.json().get('documents', [])
41
+ badges = []
42
+ for doc in docs:
43
+ fields = doc.get('fields', {})
44
+ badges.append({
45
+ 'id': doc['name'].split('/')[-1],
46
+ 'firstName': fields.get('firstName', {}).get('stringValue', ''),
47
+ 'lastName': fields.get('lastName', {}).get('stringValue', ''),
48
+ 'mission': fields.get('mission', {}).get('stringValue', ''),
49
+ 'photoUrl': fields.get('photoUrl', {}).get('stringValue', ''),
50
+ 'bgImage': fields.get('bgImage', {}).get('stringValue', ''),
51
+ 'backgroundColor': fields.get('backgroundColor', {}).get('stringValue', 'linear-gradient(135deg,#2d4a1e,#556b2f)'),
52
+ 'createdAt': fields.get('createdAt', {}).get('integerValue', 0)
53
+ })
54
+ return badges
55
+ except Exception as e:
56
+ print(f"Error fetching badges: {e}")
57
+ return []
58
 
59
+ def create_badge(data):
60
+ badge_data = {
61
+ 'fields': {
62
+ 'firstName': {'stringValue': data.get('firstName', '')},
63
+ 'lastName': {'stringValue': data.get('lastName', '')},
64
+ 'mission': {'stringValue': data.get('mission', '')},
65
+ 'photoUrl': {'stringValue': data.get('photoUrl', '')},
66
+ 'bgImage': {'stringValue': data.get('bgImage', '')},
67
+ 'backgroundColor': {'stringValue': data.get('backgroundColor', 'linear-gradient(135deg,#2d4a1e,#556b2f)')},
68
+ 'createdAt': {'integerValue': int(__import__('time').time() * 1000)}
 
69
  }
70
+ }
71
+ try:
72
+ resp = requests.post(f"{FIREBASE_URL}/badges", json=badge_data)
73
+ resp.raise_for_status()
74
+ return resp.json()
75
+ except Exception as e:
76
+ print(f"Error creating badge: {e}")
77
+ return None
78
+
79
+ def delete_badge(badge_id):
80
+ try:
81
+ resp = requests.delete(f"{FIREBASE_URL}/badges/{badge_id}")
82
+ resp.raise_for_status()
83
+ return True
84
+ except Exception as e:
85
+ print(f"Error deleting badge: {e}")
86
+ return False
87
 
88
  @app.route('/')
89
  def index():
90
  return render_template('badge.html')
91
 
92
  @app.route('/api/badges', methods=['GET'])
93
+ def get_badges_api():
94
+ badges = get_badges()
95
+ return jsonify(badges)
96
 
97
  @app.route('/api/badges', methods=['POST'])
98
+ def create_badge_api():
99
  data = request.get_json()
 
100
  if not data or not data.get('firstName') or not data.get('lastName'):
101
  return jsonify({'error': 'First name and last name are required'}), 400
102
 
103
+ result = create_badge(data)
104
+ if result:
105
+ return jsonify({'message': 'Badge created', 'data': result}), 201
106
+ return jsonify({'error': 'Failed to create badge'}), 500
 
 
 
 
 
 
 
 
 
107
 
108
+ @app.route('/api/badges/<badge_id>', methods=['DELETE'])
109
+ def delete_badge_api(badge_id):
110
+ if delete_badge(badge_id):
111
+ return jsonify({'message': 'Badge deleted'})
112
+ return jsonify({'error': 'Failed to delete badge'}), 500
 
113
 
114
  @app.route('/api/export/print')
115
  def export_print():
116
+ badges = get_badges()
117
 
118
  badges_html = ""
119
  for badge in badges:
120
+ bg_style = f"background-image: url({badge.get('bgImage')}); background-size: cover; background-position: center;" if badge.get('bgImage') else f"background: {badge.get('backgroundColor', '#2d4a1e')};"
121
+ photo = badge.get('photoUrl', 'https://via.placeholder.com/150')
122
 
123
  badges_html += f"""
124
  <div class="badge-item">
 
128
  <div class="badge-content">
129
  <div class="badge-scout-icon">โšœ๏ธ</div>
130
  <img class="badge-photo" src="{photo}" alt="">
131
+ <div class="badge-name">{badge.get('firstName', '')} {badge.get('lastName', '')}</div>
132
  </div>
133
  <div class="badge-mission-wrap">
134
  <div class="badge-mission-label">ุงู„ู…ู‡ู…ุฉ</div>
135
+ <div class="badge-mission-value">{badge.get('mission', 'โ€”')}</div>
136
  </div>
137
  </div>
138
  </div>
 
187
 
188
  @app.route('/api/export/pdf')
189
  def export_pdf():
190
+ badges = get_badges()
191
 
192
  badge_width = 8.5 * cm
193
  badge_height = 12 * cm
 
233
  c.setLineWidth(3)
234
  c.roundRect(2, 2, badge_width - 4, badge_height - 4, 6, fill=0, stroke=1)
235
 
236
+ bg_buf = get_image_buffer(badge.get('bgImage', ''), size=(int(badge_width/cm*100), int(badge_height/cm*100)))
237
  if bg_buf:
238
  c.drawImage(bg_buf, 0, 0, width=badge_width, height=badge_height, preserveAspectRatio=False, mask='auto')
239
  c.setFillColor(HexColor('#000000'))
 
257
  c.drawImage(logo_buf, badge_width/2 - 0.7*cm, center_y + 1.5*cm, width=1.4*cm, height=1.4*cm)
258
 
259
  photo_size = 2.2 * cm
260
+ photo_buf = get_image_buffer(badge.get('photoUrl', ''), size=(200, 200))
261
  if photo_buf:
262
  c.saveState()
263
  c.beginPath()
 
275
  c.setFont('ArabicBold', 24)
276
  c.drawCentredString(badge_width/2, center_y - 0.3*cm, "๐Ÿ“ท")
277
 
278
+ name = f"{badge.get('firstName', '')} {badge.get('lastName', '')}"
279
  c.setFillColor(HexColor('#ffffff'))
280
  c.setFont('ArabicBold', 12)
281
  c.setFillColor(HexColor('#000000'))
 
288
  c.setFillColor(HexColor('#3d3010'))
289
  c.rect(0, 0, badge_width, 1.3*cm, fill=1, stroke=0)
290
  draw_text_rtl(c, "ุงู„ู…ู‡ู…ุฉ", badge_width/2, 0.9*cm, 'Arabic', 9, HexColor('#c8a96e'))
291
+ mission_text = badge.get('mission', 'โ€”')
292
+ if len(mission_text) > 20:
293
+ mission_text = mission_text[:20]
294
  draw_text_rtl(c, mission_text, badge_width/2, 0.35*cm, 'ArabicBold', 10, HexColor('#f5e6b0'))
295
 
296
  c.restoreState()
 
319
 
320
  return response
321
 
 
 
 
 
322
  if __name__ == '__main__':
 
323
  app.run(debug=True, host='0.0.0.0', port=5000)
requirements.txt CHANGED
@@ -1,7 +1,7 @@
1
  flask>=2.3.0
2
- flask-sqlalchemy>=3.0.0
3
  werkzeug>=2.3.0
4
  reportlab>=4.0.0
5
  Pillow>=10.0.0
6
  arabic-reshaper>=3.0.0
7
  python-bidi>=0.6.0
 
 
1
  flask>=2.3.0
 
2
  werkzeug>=2.3.0
3
  reportlab>=4.0.0
4
  Pillow>=10.0.0
5
  arabic-reshaper>=3.0.0
6
  python-bidi>=0.6.0
7
+ requests>=2.31.0