help me on how I should think about rewriting this into an phone app:

from fastapi import FastAPI, Request, BackgroundTasks

import uvicorn
from fastapi.templating import Jinja2Templates
import numpy as np
import cv2
import random
import pygame
from pydub import AudioSegment
import requests
import logging
import os
from datetime import datetime, timedelta
import csv
import time

from roboflow import Roboflow

rf = Roboflow(api_key="xq0chu47g4AXAFzn4VDl")
project = rf.workspace().project("digital-number-ocr")
model = project.version(1).model

templates = Jinja2Templates(directory="templates")
logging.basicConfig(
    filename="app.log",  # For file output, set to None if you only want console output
    level=logging.INFO,  # For catching all info level or higher messages
    format="%(asctime)s - %(levelname)s - %(message)s",
)

TASK_STATUS = "Not started"

console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
console_handler.setFormatter(formatter)
logging.getLogger().addHandler(console_handler)

app = FastAPI()
# Paddleocr supports Chinese, English, French, German, Korean and Japanese.
# You can set the parameter `lang` as `ch`, `en`, `fr`, `german`, `korean`, `japan`
# to switch the language model in order.
# ocr = PaddleOCR(
#    use_angle_cls=False,
#    lang="en",
# )  # need to run only once to download and load model into memory


x, y, w, h = 100, 100, 200, 200
threshold = 400

# Paths to template images
window_template_path = "shot2Target.png"
dot_template_path = "dottemplate.png"

# Load template images
window_template = cv2.imread(window_template_path, 0)
dot_template = cv2.imread(dot_template_path, 0)

# Global variable to store the server’s response
SERVER_RESPONSE = None


def play_audio_file(filepath):
    pygame.mixer.init()
    pygame.mixer.music.load(filepath)
    pygame.mixer.music.play()
    while pygame.mixer.music.get_busy():  # Wait for audio to finish playing
        continue
    pygame.mixer.music.unload()
    return "played"


def check_green_spectrum(image):
    green_threshold = 0.02

    # Convert to HSV color space
    hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

    # Define range of green color in HSV
    lower_green = np.array([45, 100, 100])
    upper_green = np.array([100, 255, 200])

    # Create a binary mask where green colors are white and the rest are black
    green_mask = cv2.inRange(hsv_image, lower_green, upper_green)

    # Calculate the proportion of the image that is green
    green_ratio = np.sum(green_mask > 0) / (
        image.size / 3
    )  # image.size / 3 gives the total number of pixels in the image
    print(green_ratio)
    # Return True if there is a significant amount of green, indicating the display might be on
    return green_ratio > green_threshold


# Image processing function
async def process_images():
    global SERVER_RESPONSE, TASK_STATUS
    last_frame_change_time = datetime.now()
    stable_frame = None
    combined_result = []
    long_time_no_seen = False
    failed_frames = 0
    support = 0
    minimum_stable_time = timedelta(seconds=2.5)  # Stable time of 1.5 seconds
    frame_diff_threshold = 350  # A threshold to determine when frames are "different’
    try:
        TASK_STATUS = "In progress"
        logging.info("The process started")
        # url = "http://10.30.227.254:8088/shot.jpg"
        # frame = cv2.imread("shot(3).jpg")
        url = "http://192.168.127.124:8080/shot.jpg"
        window_match_threshold = 0.8  # Adjust according to your needs
        dot_match_threshold = 0.6  # Adjust according to your needs
        found_weight = False

        # Initialize background subtractor
        # url = "http://10.30.225.127:8080/shot.jpg"
        while True:
            img_resp = requests.get(url)
            img_arr = np.frombuffer(img_resp.content, np.uint8)
            frame = cv2.imdecode(img_arr, -1)
            current_pic = frame
            combined_result = []

            # Ensure the trackbars do not exceed the frame’s boundaries

            # cv2.imshow("Video Feed", frame)

            # Apply background subtraction
            # Convert frame to grayscale

            # cropped_img = current_pic[580 : 580 + 75, 960 : 960 + 180]
            cropped_img = current_pic[555 : 555 + 75, 995 : 995 + 180]

            if check_green_spectrum(cropped_img):
                # You can now further check for frame stability before running OCR or directly run OCR
                print("screen is on")
            else:
                print("Display appears to be off or not ready.")
                last_frame_change_time = datetime.now()
                found_weight = False
                combined_result = []

                stable_frame = None
                continue
                # Get the center of the image for rotation
            if not long_time_no_seen:
                play_audio_file("Audio/redo.mp3")
                long_time_no_seen = True
                last_frame_change_time = datetime.now()
            cropped_img_gray = cv2.cvtColor(cropped_img, cv2.COLOR_BGR2GRAY)

            image_center = tuple(np.array(cropped_img_gray.shape[1::-1]) / 2)

            ## Get the rotation matrix using the center and the angle
            rot_mat = cv2.getRotationMatrix2D(image_center, -3.5, 1.0)

            # Rotate the cropped_img_gray using the rotation matrix
            rotated_image = cv2.warpAffine(
                cropped_img_gray,
                rot_mat,
                cropped_img_gray.shape[1::-1],
                flags=cv2.INTER_LINEAR,
            )
            # Draw rectangles around detected windows
            # If a stable frame is already detected, check if the current frame differs too much

            thresholded_image = cv2.adaptiveThreshold(
                rotated_image,
                255,
                cv2.ADAPTIVE_THRESH_MEAN_C,
                cv2.THRESH_BINARY,
                11,
                11,
            )
            blurred_image = cv2.GaussianBlur(thresholded_image, (1, 1), 0)
            kernel = np.ones((3, 3), np.uint8)
            new_thresh = cv2.erode(blurred_image, kernel, iterations=1)

            # After applying the threshold to cropped_img:
            height, width = new_thresh.shape[:2]

            # Define regions within the new_thresh where the numbers are expected
            kg_region = (60, 0, 60, 70)  # Replace with actual values
            hg_region = (115, 0, 60, 70)  # Replace with actual values

            combined_region = (60, 0, 115, 70)
            combined_area = new_thresh[
                combined_region[1] : combined_region[1] + combined_region[3],
                combined_region[0] : combined_region[0] + combined_region[2],
            ]
            kg_area = new_thresh[
                kg_region[1] : kg_region[1] + kg_region[3],
                kg_region[0] : kg_region[0] + kg_region[2],
            ]
            hg_area = new_thresh[
                hg_region[1] : hg_region[1] + hg_region[3],
                hg_region[0] : hg_region[0] + hg_region[2],
            ]

            if stable_frame is not None:
                frame_diff = cv2.absdiff(combined_area, stable_frame)
                non_zero_count = np.count_nonzero(frame_diff)
                print("heythere")
                print(non_zero_count)
                if non_zero_count > frame_diff_threshold:
                    if support > 2:
                        play_audio_file("Audio/support.mp3")
                    elif failed_frames > 3:
                        random_number = random.randint(1, 2)
                        play_audio_file(f"Audio/waiting{random_number}.mp3")
                        failed_frames = 0
                        support += 1
                    # A significant difference is detected, update the stable frame and reset timestamp
                    last_frame_change_time = datetime.now()
                    stable_frame = np.copy(combined_area)
                    print("Frame has changed significantly. Updating stable frame.")
                    failed_frames += 1

            else:
                # No stable frame yet, so assign one and start the timer
                stable_frame = np.copy(combined_area)

            # Check if the frame has been stable for at least 1.5 seconds
            print(datetime.now() - last_frame_change_time)
            if (
                datetime.now() - last_frame_change_time >= minimum_stable_time
                and found_weight == False
            ):
                cv2.imwrite("combined_area.png", cropped_img)
                print("Frame has been stable for 1.5 seconds. Running OCR.")
                # kg_result = await check_picture(kg_area)
                # hg_result = await check_picture(hg_area)
                if combined_result == []:
                    combined_result = await check_picture(combined_area)
                    print(combined_result)
                if combined_result != []:
                    if "-" in combined_result:
                        combined_result = combined_result.replace("-", ".")
                    formatted_combined_result = "{:.2f}".format(float(combined_result))
                    hey_there = await generate_audio(formatted_combined_result)
                    print(f"{hey_there}")
                    play_audio_file("audio.mp3")
                    print(f"combined: {formatted_combined_result}")
                    await update_csv(formatted_combined_result)
                #
                #    # Reset stable frame and timestamp after running OCR
                failed_frames = 0
                support = 0
                found_weight = True
                stable_frame = None
            # Show the frame
            time.sleep(0.5)

    except Exception as e:
        TASK_STATUS = "Failed"
        SERVER_RESPONSE = str(e)
        logging.error(
            "An error occurred during image processing: {0}".format(e), exc_info=True
        )


@app.post("/start_processing")
async def start_processing(background_tasks: BackgroundTasks):
    logging.info("Received start_processing request")
    background_tasks.add_task(process_images)
    return {"message": "Image processing started"}


@app.get("/get_response")
async def get_response():
    return {"response": SERVER_RESPONSE}


@app.get("/task_status")
def get_task_status():
    return {"status": TASK_STATUS}


async def generate_audio(clenedUpResponse):
    global TASK_STATUS
    already_exists = False

    while float(clenedUpResponse) > 30.0:
        clenedUpResponse = float(clenedUpResponse) / 10
    if "." in clenedUpResponse:
        clenedUpResponse = clenedUpResponse.replace(".", " komma ")

    combined_audio_seg = AudioSegment.empty()
    # Generate and concatenate audio for each word
    audio_seg = AudioSegment.from_mp3("Audio/lagtin.mp3")
    combined_audio_seg += audio_seg
    for text in clenedUpResponse.split():
        audio_filename = f"Audio/{text}.mp3"
        audio_seg = AudioSegment.from_mp3(audio_filename)
        combined_audio_seg += audio_seg
    # Export the combined audio file
    audio_seg = AudioSegment.from_mp3("Audio/kilo.mp3")
    combined_audio_seg += audio_seg
    combined_audio_filename = "audio.mp3"
    combined_audio_seg.export(combined_audio_filename, format="mp3")

    return combined_audio_filename


@app.get("/audio")
async def play_audio():
    play_audio_file("audio.mp3")
    return {"message": "Audio playback started on server"}


@app.get("/video")
async def video_endpoint(request: Request):
    return templates.TemplateResponse("video.html", {"request": request})


async def check_picture(image_array):
    # Convert the Numpy array to an image file-like object
    is_success, buffer = cv2.imencode(".jpg", image_array)
    if not is_success:
        return None  # Handle errors here
    # Convert the buffer to bytes
    image_bytes = buffer.tobytes()
    try:
        result = model.predict("combined_area.png", confidence=40, overlap=30).json()

        # Sort the predictions by the ‘x’ value
        sorted_predictions = sorted(result["predictions"], key=lambda k: k["x"])

        # Update the original result with the sorted predictions
        result["predictions"] = sorted_predictions

        # Initialize an empty list to store the classes from the sorted predictions
        classes = []

        # Loop through each sorted prediction and add the “class” to the classes list
        for prediction in sorted_predictions:
            classes.append(prediction["class"])

        # Join the classes into a single string
        classes_string = "".join(classes)
        print(classes_string)
    except:
        print("no visable numbers")
        new_result = []
    return classes_string


async def update_csv(result):
    # Ensure result is not None and can be converted to float
    if result is None:
        print("No result to update CSV.")
        return
    today = datetime.now().strftime("%Y-%m-%d")
    #    Check if the file exists
    file_name = f"Elektronik_{today}.csv"
    file_exists = os.path.isfile(file_name)
    #    If the file doesn't exist, create a new one with the header row
    if not file_exists:
        with open(file_name, "w", newline="") as csvfile:
            writer = csv.writer(csvfile, delimiter=",", quotechar='"')
            writer.writerow(["Vikt", "Datum", "Total Vikt"])
            csvfile.close()
    #    Read the existing CSV file
    with open(file_name, "r") as csvfile:
        reader = csv.reader(csvfile, delimiter=",", quotechar='"')
        rows = list(reader)
    #    Check if the date already exists
    #    Add the weights for the current date
    total_weight = 0
    for row in rows:
        if row[1] == today:
            total_weight += float(row[0])
    try:
        while float(result) > 30.0:
            result = float(result) / 10
    except ValueError as ve:
        print(f"Error converting result to float: {ve}")
        # Handle the error or return from the function
    # Round the result to 2 decimal places
    result = round(float(result), 2)
    total_weight += float(result)
    rows.append([str(result), today, round(total_weight, 2)])
    #    Write the updated rows to the CSV file
    with open(file_name, "w", newline="") as csvfile:
        writer = csv.writer(csvfile, delimiter=",", quotechar='"')
        writer.writerows(rows)
    return "Done"


if __name__ == "__main__":
    uvicorn.run(app, host="localhost", port=3333)