def load_product_data(): new_product_data = {} categories = fireClient.collection("categories").stream() for category_obj in categories: category = category_obj.to_dict() category_id = category["category_id"] new_product_data[category_id] = category.copy() # make a copy of keys to prevent concurrent modification, reason being we need to convert string key to numbers prices = list(new_product_data[category_id]["prices"].keys()) for price in prices: new_product_data[category_id]["prices"][float( price)] = new_product_data[category_id]["prices"][price] new_product_data[category_id]["prices"].pop(price) new_product_data[category_id]["products"] = {} products = fireClient.collection("categories").document( category_id).collection("products").stream() for product_obj in products: product = product_obj.to_dict() product_id = product["product_id"] new_product_data[category_id]["products"][ product_id] = product.copy() global product_data product_data = new_product_data return product_data
def create_template(): if "user" not in session or "uid" not in session["user"]: return redirect( url_for("auth.login", redirect=url_for("sms.create_template"))) user_obj = fireClient.collection("users").document(session["uid"]).get() if not user_obj.exists or user_obj.to_dict().get("tier", "") != "director": abort(401) if request.method == "GET": return render_template("create_template.html", available_data=access_property) template = { "template": request.form["template"], "author": { "uid": session["user"]["uid"], "name": session["user"]["display_name"] }, "generated": False, "recipients": { user.uid: False for user in firebase_auth.list_users().iterate_all() if user.uid not in director_uids }, "UTC_timestamp": str(datetime.utcnow()) } template_obj = fireClient.collection("message_templates").document() fireClient.collection("message_templates").document( template_obj.id).set(template) return redirect(url_for("sms.view_template", template_id=template_obj.id))
def display_directory(): directory_ref = fireClient.collection("info").document("directory") directory = directory_ref.get().to_dict() directors = [(firebase_auth.get_user(directory["directors"][role]), role) for role in director_roles if role in directory["directors"]] chapters_dict = fireClient.collection("info").document("chapter_info").get().to_dict()["chapters"] chapters = sorted(chapters_dict.items(), key=lambda entry: entry[1]) return render_template("directory.html", directors=directors, director_names=director_titles, chapters=chapters)
def admin_delivered_view(): delivered_orders = [ order.to_dict() for order in fireClient.collection("orders").where( "status.delivered", "==", True).order_by("UTC_timestamp").stream() ] return render_template("admin_delivered_view.html", delivered=delivered_orders)
def edit_profile(): if "uid" not in session: return redirect(url_for("auth.login")) uid = session["uid"] user = firebase_auth.get_user(session["uid"]) if request.method == "GET": return render_template("edit_profile.html", user=user) try: if session["uid"] != request.form["uid"]: abort(400, "UID does not match session") phone = ''.join(c for c in request.form["phone"] if c.isdigit() or c == '+') if "+" not in phone: phone = "+1" + phone if user.display_name and request.form["name"] != user.display_name: user_ref = fireClient.collection("users").document(user.uid) user_dict = user_ref.get().to_dict() user_dict["name_array"] = request.form["name"].lower().split() user_ref.set(user_dict) firebase_auth.update_user( uid=session["uid"], display_name=request.form["name"], email=request.form["email"], phone_number=phone ) except Exception as e: abort(400, "Malformed edit profile request: " + str(e)) return redirect(url_for("info.view_profile", uid=session["uid"]))
def deliver_item(): if "uid" not in session: return redirect(url_for("auth.login")) if request.method == "GET": return render_template("delivery_form.html", orderID=request.args.get("orderID"), address=request.args.get("address"), price=request.args.get("price")) orderID = request.form["orderID"] uid = session["uid"] # simply mark as delivered order_ref = fireClient.collection("orders").document(orderID) order_obj = order_ref.get() if not order_obj.exists: abort(404, "Order not found") order_dict = order_obj.to_dict() order_dict["status"]["delivered"] = True order_dict["delivery"] = { "deliveredBy": { "uid": uid, "name": session["name"] }, "UTC_timestamp": str(datetime.utcnow()) } order_ref.set(order_dict) return redirect(url_for("bakesale.delivery_view"))
def invoice_item(): if "uid" not in session: return redirect(url_for("auth.login")) if request.method == "GET": return render_template("invoice_form.html", orderID=request.args.get("orderID"), price=request.args.get("price")) orderID = request.form["orderID"] invoiceLink = request.form["invoiceLink"] financeID = session["uid"] if not orderID or not financeID: abort(400, "Invoice request must include order ID and login") order_ref = fireClient.collection("orders").document(orderID) order_obj = order_ref.get() if not order_obj.exists: abort(404, "Order not found") order_dict = order_obj.to_dict() if order_dict["status"]["invoiced"] or "invoice" in order_dict: abort(400, "Order already invoiced") order_dict["invoice"] = {"link": invoiceLink, "creator": financeID} order_dict["status"]["invoiced"] = True order_ref.set(order_dict) return redirect(url_for("bakesale.finance_view"))
def report_issue(): if request.method == "GET": return render_template("issue_form.html", orderID=request.args.get("orderID"), view=request.args.get("view")) issue = { "description": request.form["description"], "UTC_timestamp": str(datetime.utcnow()), "resolved": False } orderID = request.form["orderID"] order_ref = fireClient.collection("orders").document(orderID) order_obj = order_ref.get() if not order_obj.exists: abort(404, "Order not found") order_dict = order_obj.to_dict() if "issues" in order_dict: order_dict["issues"].append(issue) else: order_dict["issues"] = [issue] order_dict["has_unresolved_issues"] = True order_ref.set(order_dict) flash( "We are sorry that you had an issue with this order, a member of the SCAN team will look at it as soon as possible. Thank you." ) return redirect( url_for("bakesale.show_order", orderID=orderID, view=request.form["view"]))
def view_templates(): message_templates = [ template for template in fireClient.collection("message_templates").order_by( "UTC_timestamp", direction=firestore.Query.DESCENDING).stream() ] return render_template("view_templates.html", templates=message_templates)
def view_chapter(chapter_id): chapter_ref = fireClient.collection("info").document("chapter_info").collection("chapters").document(chapter_id) chapter_obj = chapter_ref.get() if not chapter_obj.exists: abort(404, "Chapter not found") officers = [(firebase_auth.get_user(chapter_obj.get("officers")[role]), role) for role in officer_roles] return render_template("chapter_directory.html", chapter=chapter_obj.to_dict(), chapter_id=chapter_id, officers=officers, officer_titles=officer_titles)
def generate_template(template_id): template_ref = fireClient.collection("message_templates").document( template_id) template_obj = template_ref.get() if not template_obj.exists: abort(404, "Template not found") template = template_obj.to_dict() if template["generated"]: abort(410, "Template's messages have already been generated") new_messages_batch = fireClient.batch() num_errors = 0 error_threshold = 0.05 ctx = { "leaderboard": fireClient.collection("statistics").document("sales").get().get( "leaderboard") } for uid in template["recipients"]: # If the error rate for message generation exceeds 5%, stop it if num_errors / len(template["recipients"]) > error_threshold: break try: msg_ref = fireClient.collection("messages").document() user = firebase_auth.get_user(uid) msg = { "content": render_message(template["template"], user, ctx), "sender": random.choice(director_uids), "recipient": uid, "phone": user.phone_number, "sent": False, "template_id": template_id } new_messages_batch.set(msg_ref, msg) except: num_errors += 1 if num_errors / len(template["recipients"]) > error_threshold: abort( 400, "The error rate for generating messages exceeded %0.2f%%, generation cancelled" % (100 * error_threshold)) else: new_messages_batch.commit() template["generated"] = True template_ref.set(template) return redirect( url_for("sms.view_template", template_id=template_obj.id))
def view_profile(uid): user_ref = fireClient.collection("users").document(uid) user_obj = user_ref.get() if not user_obj.exists: abort(404, "User not found") user_dict = user_obj.to_dict() return render_template("view_profile.html", user=user_dict, rolenames=role_names)
def view_template(template_id): template_ref = fireClient.collection("message_templates").document( template_id) template_obj = template_ref.get() if not template_obj.exists: abort(404, "Template not found") return render_template("view_template.html", template=template_obj.to_dict(), template_obj=template_obj)
def is_verified(uid): try: firebase_auth.get_user(uid) except: return False try: return fireClient.collection("users").document(uid).get().get("tier") == "director" except: return False
def display_leaderboard(): leaderboard = fireClient.collection("statistics").document( "sales").get().to_dict()["leaderboard"] return render_template("leaderboard.html", leaderboard={ referral: leaderboard[referral] for referral in leaderboard if referral.lower() not in exec_referral_links })
def delivery_view(): if "uid" not in session: return redirect(url_for("auth.login")) baked_orders = fireClient.collection("orders").where( "status.baked", "==", True).where("status.delivered", "==", False).order_by("UTC_timestamp").stream() return render_template("delivery_view.html", baked=[order.to_dict() for order in baked_orders])
def finance_view(): if "uid" not in session: return redirect(url_for("auth.login")) not_invoiced_orders = fireClient.collection("orders").where( "status.invoiced", "==", False).order_by("UTC_timestamp").stream() return render_template( "finance_view.html", not_invoiced_orders=[order.to_dict() for order in not_invoiced_orders])
def admin_view(): if "uid" not in session or session["uid"] not in admin_uids: return redirect(url_for("auth.login")) issue_orders = [ order.to_dict() for order in fireClient.collection("orders").where( "has_unresolved_issues", "==", True).order_by( "UTC_timestamp").stream() ] return render_template("admin_view.html", issue_orders=issue_orders)
def view_messages(): if "uid" not in session: return redirect( url_for("auth.login", redirect=url_for("sms.view_messages"))) my_messages = { message.id: message.to_dict() for message in fireClient.collection("messages").where( "sender", "==", session["uid"]).where("sent", "==", False).stream() } return render_template("messages.html", messages=my_messages)
def add_vegan_products(): for category_obj in fireClient.collection("categories").stream(): category = category_obj.to_dict() category_id = category["category_id"] if category_id.startswith("r_"): continue # copying gluten free products: gf_ new_category_id = "p_" + category_id[3:] new_category_data = { "name": category["name"].replace("Gluten Free", "Vegan"), "category_id": new_category_id, "prices": category["prices"].copy() } # fireClient.collection("categories").document(new_category_id).set(new_category_data) # print(new_category_id) for product_obj in fireClient.collection("categories").document(category_id).collection("products").stream(): product = product_obj.to_dict() new_product = product.copy() new_product["product_id"] = "p_" + new_product["product_id"][3:]
def mark_sent(): sent_messages = request.form.getlist("sent") if len(sent_messages) < 1: return redirect(url_for("sms.view_messages")) for message_id in sent_messages: message_ref = fireClient.collection("messages").document(message_id) message_obj = message_ref.get() if not message_obj.exists: continue message = message_obj.to_dict() message["sent"] = True message_ref.set(message) template_ref = fireClient.collection("message_templates").document( message["template_id"]) template_obj = template_ref.get() if not template_obj.exists: continue template = template_obj.to_dict() template["recipients"][message["recipient"]] = True template_ref.set(template) return redirect(url_for("sms.view_messages"))
def baker_view(): # check login if "uid" not in session: return redirect(url_for("auth.login")) # load in all pending orders pending_orders = fireClient.collection("orders").where( "status.invoiced", "==", True).where("status.baked", "==", False).order_by("UTC_timestamp").stream() return render_template( "baker_view.html", product_data=product_data, pending=[order.to_dict() for order in pending_orders])
def add_chapter(): if "uid" not in session or not is_verified(session["uid"]): return redirect(url_for("auth.login", redirect=url_for("info.add_chapter"))) if request.method == "GET": return render_template("create_chapter.html") if not request.json or "name" not in request.json or "officers" not in request.json or any(role not in request.json["officers"] for role in officer_roles): abort(400, "Malformed chapter creation request") try: for role in officer_roles: firebase_auth.get_user(request.json["officers"][role]) except: abort(404, "Officer UIDs not found") chapters_info_obj = fireClient.collection("info").document("chapter_info").get() chapter_id = chapters_info_obj.get("id_counter") chapter_id = str(chapter_id) while len(chapter_id) < 3: chapter_id = "0" + chapter_id chapter_dict = chapters_info_obj.get("chapters") chapter_dict[request.json["name"]] = chapter_id fireClient.collection("info").document("chapter_info").set( { "id_counter": int(chapter_id) + 1, "chapters": chapter_dict } ) chapter_ref = fireClient.collection("info").document("chapter_info").collection("chapters").document(chapter_id) try: officers = request.json["officers"] chapter = { "name": request.json["name"], "officers": { role: officers[role] for role in officer_roles }, "ambassadors": [] } except KeyError: abort(400, "Malformed chapter creation request") batch_write = fireClient.batch() batch_write.set(chapter_ref, chapter) for uid in officers.values(): user_ref = fireClient.collection("users").document(uid) old_user = user_ref.get().to_dict() old_user["tier"] = "officer" batch_write.set(fireClient.collection("users").document(uid), old_user) batch_write.commit() return redirect(url_for("info.view_chapter", chapter_id=chapter_id))
def show_order(orderID): order_ref = fireClient.collection("orders").document(orderID) order_obj = order_ref.get() if not order_obj.exists: abort(404, "Order not found") view = request.args.get("view", default="customer") order_dict = order_obj.to_dict() if order_dict["status"]["delivered"]: order_dict["statusText"] = "Delivered" elif order_dict["status"]["baked"]: order_dict["statusText"] = "Baked" elif order_dict["status"]["invoiced"]: order_dict["statusText"] = "Invoiced" else: order_dict["statusText"] = "Received" if "uid" in session: if view == "finance": return render_template("single_order_finance.html", order=order_dict, product_data=product_data) elif view == "baker": return render_template("single_order_baker.html", order=order_dict, product_data=product_data) elif view == "delivery": return render_template("single_order_delivery.html", order=order_dict, product_data=product_data) elif view == "admin" and session["uid"] in admin_uids: return render_template("single_order_admin.html", order=order_dict, product_data=product_data) return render_template("single_order.html", order=order_dict, product_data=product_data)
def create_user(): if not request.form: abort(400, "Request must include form data") try: new_user = { "email": request.form["email"], "first_name": request.form["first_name"], "last_name": request.form["last_name"], "grade": request.form["grade"], "school": request.form["school"], "phone": request.form["phone"] } except: abort(400, "Malformed form data") if not new_user["phone"]: new_user.pop("phone") elif "+" not in new_user["phone"]: new_user["phone"] = "+1" + new_user["phone"] try: firebase_user = firebase_auth.create_user( email=new_user["email"], email_verified=False, phone_number=new_user.get("phone", None), password="******", display_name=str("%s %s" % (new_user["first_name"], new_user["last_name"]))) user_ref = fireClient.collection("users").document(firebase_user.uid) user_ref.set({ "name_array": firebase_user.display_name.lower().split(), "tier": "member" }) except BaseException as e: abort(400, "User could not be created: %s" % str(e)) return {"success": True}
def resolve_issue(): if "uid" not in session or session["uid"] not in admin_uids: return redirect(url_for("auth.login")) if request.method == "GET": return render_template( "resolve_issue_form.html", orderID=request.args.get("orderID"), issue_description=request.args.get("issue_description"), issue_index=request.args.get("issue_index")) orderID = request.form["orderID"] issue_index = int(request.form["issue_index"]) order_ref = fireClient.collection("orders").document(orderID) order_obj = order_ref.get() if not order_obj.exists: abort(404, "Order not found") order = order_obj.to_dict() if issue_index < 0 or issue_index >= len(order["issues"]): abort(400, "Invalid issue index") order["issues"][issue_index]["resolved"] = True order["issues"][issue_index]["resolved_by"] = { "uid": session["uid"], "name": session["name"] } all_resolved = True for issue in order["issues"]: if not issue["resolved"]: all_resolved = False order["has_unresolved_issues"] = not all_resolved order_ref.set(order) return redirect(url_for("bakesale.admin_view"))
def bake_item(): if "uid" not in session: return redirect(url_for("auth.login")) if request.method == "GET": return render_template("bake_form.html", product_data=product_data, orderID=request.args.get("orderID"), itemCategory=request.args.get("itemCategory"), itemID=request.args.get("itemID"), qtyMax=request.args.get("qtyMax")) orderID = request.form["orderID"] itemCategory = request.form["itemCategory"] itemID = request.form["itemID"] quantity = int(request.form["quantity"]) bakerID = session["uid"] if orderID is None or itemCategory is None or itemID is None or quantity is None \ or itemCategory not in product_data or not any([itemID in products for category in product_data for products in product_data[category]["products"]]): abort( 400, "Bake request must include order ID, category, item, and quantity") order_ref = fireClient.collection("orders").document(orderID) order_obj = order_ref.get() if not order_obj.exists: abort(404, "Order not found") order_dict = order_obj.to_dict() if itemID not in order_dict["fulfillment"][itemCategory]: abort(400, "Item not in order") if quantity <= 0 or quantity > ( order_dict["quantities"][itemCategory][itemID] - order_dict["fulfillment"][itemCategory][itemID]["count"]): abort(400, "Quantity is invalid") order_dict["fulfillment"][itemCategory][itemID]["count"] += quantity if bakerID in order_dict["fulfillment"][itemCategory][itemID]["bakers"]: order_dict["fulfillment"][itemCategory][itemID]["bakers"][bakerID][ "count"] += quantity else: user_name = session["name"] order_dict["fulfillment"][itemCategory][itemID]["bakers"][bakerID] = { "count": quantity, "name": user_name } # check to see if all quantities are met allMet = True for category in order_dict["quantities"]: for item in order_dict["quantities"][category]: if order_dict["fulfillment"][category][item]["count"] < order_dict[ "quantities"][category][item]: allMet = False break if allMet: order_dict["status"]["baked"] = allMet order_dict["baking"] = { "UTC_timestamp": str(datetime.utcnow()), } order_ref.set(order_dict) return redirect(url_for("bakesale.baker_view"))
def update_leaderboard(referral_link, amount): leaderboard_ref = fireClient.collection("statistics").document("sales") sales = leaderboard_ref.get().to_dict() sales["leaderboard"][referral_link] = sales["leaderboard"].get( referral_link, 0.0) + amount leaderboard_ref.set(sales)
"finance", "operations" ] officer_roles = [ "communications", "publicity", "executive", "finance", "operations" ] user_lookup_methods = { "phone": lambda phone: [firebase_auth.get_user_by_phone_number(phone if "+" in phone else "+1" + phone)], "email": lambda email: [firebase_auth.get_user_by_email(email)], "name": lambda name: [firebase_auth.get_user(user.id) for user in fireClient.collection("users").where("name_array", "array_contains_any", name.lower().split()).stream()] } def is_verified(uid): try: firebase_auth.get_user(uid) except: return False try: return fireClient.collection("users").document(uid).get().get("tier") == "director" except: return False @info.route("/") def index(): return redirect(url_for("info.display_directory"))
import random from flask import Blueprint, request, abort, jsonify, render_template, redirect, url_for, session import firebase_admin.auth as firebase_auth import firebase_admin.firestore as firestore from scan_web import fireClient from datetime import datetime sms = Blueprint("sms", __name__, template_folder="sms_templates") ambassador_uids = [ ambassador.id for ambassador in fireClient.collection("users").where( "tier", "==", "ambassador").stream() ] officer_uids = [ officer.id for officer in fireClient.collection("users").where( "tier", "==", "officer").stream() ] director_uids = [ director.id for director in fireClient.collection("users").where( "tier", "==", "director").stream() ] @sms.route("/") def index(): return redirect(url_for("sms.view_messages")) @sms.route("/messages") def view_messages(): if "uid" not in session: