data-analytics/minigolf-demo/backend/main.py (102 lines of code) (raw):

import functions_framework import firebase_admin from firebase_admin import firestore from google.cloud import bigquery, storage from flask import make_response import base64 import matplotlib.pyplot as plt from io import BytesIO from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas import pandas as pd PROJECT_ID = "" BACKGROUND_IMAGE_BUCKET = "" VIDEO_BUCKET = "" BQ_DATASET = "" BQ_PREFIX = f"{PROJECT_ID}.{BQ_DATASET}" if not firebase_admin._apps: firebase_admin.initialize_app(options={'projectId':f'{PROJECT_ID}'}) db = firestore.client() bq_client = bigquery.Client() def get_user_status(user_id): """Retrieves the status of a user from Firestore.""" user_doc_ref = db.collection('users_a').document(user_id) user_doc = user_doc_ref.get() if not user_doc.exists: return None # Return None if user not found return user_doc.to_dict().get('status') def get_commentary(user_id): """Fetches commentary for a user from BigQuery.""" query = f""" SELECT commentary FROM `{BQ_PREFIX}.commentary` WHERE user_id = '{user_id}' """ query_job = bq_client.query(query) results = list(query_job) return results[0].commentary if results else None def get_stat(user_id): """Calculates and returns game statistics.""" query = f"SELECT * FROM {BQ_PREFIX}.tracking" df = bq_client.query(query).to_dataframe() last_frame_per_user = df.groupby('user_id')['frame_number'].transform(max) df_filtered = df[df['frame_number'] == last_frame_per_user] df_filtered = df_filtered[df_filtered['distance'] < 30] user_shot_counts = df_filtered.groupby('user_id')['shot_number'].first() user_shot_counts = user_shot_counts[user_shot_counts > 0] shot_number_freq = user_shot_counts.value_counts() # Selected user's number of shots num_users = df['user_id'].nunique() user_shots = user_shot_counts.get(user_id, 0) average_shots_per_user = user_shot_counts.mean() plt.figure(figsize=(8, 6)) # Set figure size plt.xlim(0, 9) barlist = plt.bar(shot_number_freq.index, shot_number_freq.values, color='#4285F4') plt.xlabel('Number of Shots') plt.ylabel('Number of Users') plt.title('Distribution of Number of Shots per User') if user_shots in shot_number_freq.index: barlist[shot_number_freq.index.get_loc(user_shots)].set_color('#34A853') plt.legend([barlist[shot_number_freq.index.get_loc(user_shots)]], [f'Shot Number of {user_id}']) plt.xticks(range(9)) # Save the plot to a BytesIO object fig = plt.gcf() canvas = FigureCanvas(fig) output = BytesIO() canvas.print_png(output) plt.close(fig) # Close the figure to prevent memory leaks # Encode the image in Base64 base64_encoded_image = base64.b64encode(output.getvalue()).decode('utf-8') # --- Construct the stat dictionary --- stat = { 'num_users': num_users, 'user_shots': user_shots, 'average_shots': f'{average_shots_per_user:.2f}', 'barchart': base64_encoded_image } return stat @functions_framework.http def get_user_data(request): """HTTP Cloud Function. Args: request (flask.Request): The request object. <https://flask.palletsprojects.com/en/1.1.x/api/#incoming-request-data> Returns: The response text, or any set of values that can be turned into a Response object using `make_response` <https://flask.palletsprojects.com/en/1.1.x/api/#flask.make_response>. """ request_args = request.args if request_args and 'userId' in request_args: user_id = request_args['userId'] else: return {'error': 'userId parameter is required'}, 400 try: image_url = None commentary = None video_url = None stat = None status = get_user_status(user_id) if not status: return {'error': 'User is not existed'}, 400 if status != "processing": image_url = f"https://storage.cloud.google.com/{BACKGROUND_IMAGE_BUCKET}/{user_id}.png" video_url = f"https://storage.cloud.google.com/{VIDEO_BUCKET}/{user_id}.mp4" commentary = get_commentary(user_id) stat = get_stat(user_id) response_data = { 'status': status, 'imageUrl': image_url, 'commentary': commentary, 'videoUrl': video_url, 'users': int(stat['num_users']), 'shots': int(stat['user_shots']), 'average': stat['average_shots'], 'barchart': stat['barchart'] } response = make_response(response_data) response.headers.add('Access-Control-Allow-Origin', 'http://127.0.0.1:5501') response.headers['Content-Type'] = 'application/json' return response, 200 except Exception as e: print(f"Error fetching user status: {e}") return {'error': 'Failed to fetch user status'}, 500