# Project export: FastTrackFDA

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2024
- Tagline: Accelerating Medical Device Innovations from Concept to Market
- Devpost: https://devpost.com/software/fasttrackfda
- GitHub: https://github.com/sejalgupta/tree_hacks_fda_approval
- Video: https://www.youtube.com/embed/sZxUD_5z4YM?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 4 GitHub contributor(s) — sejalgupta4902 (52 commits), Prabhakar Kafle (27 commits), natashamaniarnew (16 commits), sejalgupta (2 commits)

## Devpost submission (written by the team)

### Inspiration

Medical device companies spend on average between $20-40K on FDA consulting services and around 9 months to complete just the 510k approval process which allows you to legally market your device. With the rise of software enabled devices (such as wearables etc), the development time for these devices are decreasing while regulatory timelines haven’t changed, hindering and even discouraging startups from going through the FDA process. Our team of 3 MIT graduate students have previously developed wearable medical devices and spent months designing study protocols, revising IRB applications, and doing literature review before even starting pilot testing of the device. After numerous conversations with other medical device entrepreneurs and FDA consulting services, we uncovered widespread frustrations with the FDA's overwhelming, non-intuitive, and scattered information landscape, particularly for medical devices. Driven by these challenges, we developed FastTrackFDA—a transformative platform engineered to streamline the FDA journey starting from device conception and guiding development until FDA clearance.

### What it does

Demystifying the FDA journey, our platform empowers medical device startups and companies by guiding them seamlessly through the approval process. It aids in identifying predicate devices and crafting clinical trial protocols, significantly reducing the reliance on consultants. The FDA's website, often criticized for its cluttered guidance documents, constant revisions, voluminous and complex databases, and lack of personalization, poses a significant challenge, especially for medical device manufacturers. Our solution is an intuitive user interface, crafted with input from device developers, designed to streamline the description and intended use case of your device. It enables a comprehensive understanding of all market entry requirements, facilitating the creation of predicate devices and clinical trial designs. This approach not only accelerates the approval process and reduces costs but also diminishes the dependence on external consulting services. (1) Finding Substantially Equivalent Devices: Identifying a substantially equivalent device is crucial in determining the appropriate regulatory pathway for your device. For FDA 510(k) approval, it's imperative that your device is matched with a substantially equivalent 'predicate' device. Traditionally, consultants might spend 20-40 hours on this task alone. Our platform leverages vector search and matrix similarities, to pinpoint the most compatible predicate devices, using a semantic comparison with your device. (2) Personalizing the Regulatory Workflow: With the FDA's mandate from October 2023 requiring all 510(k) submissions to follow an e-submission template, our platform standardizes yet personalizes the steps necessary for compliance. (3) Generating Clinical Trial Designs: To streamline the development of study designs, our platform displays clinical trials for devices that are semantically similar to yours, sourced from the clinicaltrials.gov database. We then generate potential designs, incorporating inclusion/exclusion criteria, intervention modules (control and experimental groups), study procedures, and outcome measures, based on a comprehensive analysis of clinical trial data. This refined approach not only clarifies the path to FDA approval but also positions our platform as a pivotal tool in bringing medical devices to market more efficiently and cost-effectively.

### How we built it

Our application consists of a Next.Js frontend and Flask backend. For generating our dataset of all the 510k summaries, we developed our own custom pdf extractor to extract different sections and tables from the 510K summary documents on the FDA site. In order to find the best predicate device we developed a matrix similarity algorithm using the vector similarity search built off of our data storage in Pinecone. Then we used openai GPT-4-turbo to generate comparison tables among the two devices which is a section of the 510k document. To visualize the database (in order for users to see where their device lies in the space of all similar devices), we used Nomic’s Atlas module. Lastly, we automatically collected a dataset of clinical trial designs from clinicaltrials.gov and used Together API to finetune a collection of clinical trials generation models and also used openAI’s LLM for trial generation.

### Challenges we ran into

Table extraction: Each PDF document for 510k summaries has a different format (some are scanned copies and some are pdfs with differently formatted tables and various headers that aren’t standard). We tried multiple approaches using computer vision, ocr, table extraction libraries, and heuristics and ultimately creating our own custom approach that ran faster than the other approaches in order to populate our database with all of the previous records of 510ks. Deployment: Due to some dependencies, we had an issue with deploying the full application so we could deploy the frontend and backend separately.

### Accomplishments we're proud of

We’re really proud of actually solving a use case that we’ve seen a huge need for first hand and no real solution. We truly believe that cost, time, or ambiguity should not be the reason that a medical device which can transform a person’s life doesn’t get to market soon enough.

### What we learned

Prioritization of features and shipping an MVP that addresses the most impactful ones Table extraction among pdfs isn’t a solved task and there are many nuances The FDA process is very complex

### What's next

Our overall mission is to be a central platform for all device companies to start using when they begin conception of a device. Future steps that we plan to build out soon are: Expanding to other areas of the FDA process such as quality Assurance: 75% of the time that a consultant spends is revising existing drafted material and making sure it abides by the Refusal to Accept checklist documentation and guidance documents for each section. We plan to user our table extraction tool to extract this RTA document and do automatic checking before the person creates the table. Improve table extraction to be more generalizable: We hope users can ask interactive questions about guidance documents but only once they’ve been parsed perfectly. Continuous learning and multi-agent reviewal- Since newer FDA device approvals are most important, we plan to integrate a continuous learning approach that constantly updates the data as soon as a new device is approved. Multi-agent reviewal: For each part of this workflow, we hope to embed more specific knowledge about the decision making process of a FDA consultant. For example, for determining clinical trial design we hope to embed an “clinical trial expert” agent that checks whether the trial’s inclusion/ exclusion reduces bias, its trial design is safe, and potential suggestions.

## README (from the GitHub repository)

**Inspiration**

Medical device companies spend on average between $20-40K on FDA consulting services and around 9 months to complete just the 510k approval process which allows you to legally market your device. With the rise of software enabled devices (such as wearables etc), the development time for these devices are decreasing while regulatory timelines haven’t changed, hindering and even discouraging startups from going through the FDA process. Our team of 3 MIT graduate students have previously developed wearable medical devices and spent months designing study protocols, revising IRB applications, and doing literature review before even starting pilot testing of the device.

After numerous conversations with other medical device entrepreneurs and FDA consulting services, we uncovered widespread frustrations with the FDA's overwhelming, non-intuitive, and scattered information landscape, particularly for medical devices. Driven by these challenges, we developed FastTrackFDA—a transformative platform engineered to streamline the FDA journey starting from device conception and guiding development until FDA clearance.

**What it does**

Demystifying the FDA journey, our platform empowers medical device startups and companies by guiding them seamlessly through the approval process. It aids in identifying predicate devices and crafting clinical trial protocols, significantly reducing the reliance on consultants. The FDA's website, often criticized for its cluttered guidance documents, constant revisions, voluminous and complex databases, and lack of personalization, poses a significant challenge, especially for medical device manufacturers. Our solution is an intuitive user interface, crafted with input from device developers, designed to streamline the description and intended use case of your device. It enables a comprehensive understanding of all market entry requirements, facilitating the creation of predicate devices and clinical trial designs. This approach not only accelerates the approval process and reduces costs but also diminishes the dependence on external consulting services. (1) Finding Substantially Equivalent Devices: Identifying a substantially equivalent device is crucial in determining the appropriate regulatory pathway for your device. For FDA 510(k) approval, it's imperative that your device is matched with a substantially equivalent 'predicate' device. Traditionally, consultants might spend 20-40 hours on this task alone. Our platform leverages vector search and matrix similarities, to pinpoint the most compatible predicate devices, using a semantic comparison with your device. (2) Personalizing the Regulatory Workflow: With the FDA's mandate from October 2023 requiring all 510(k) submissions to follow an e-submission template, our platform standardizes yet personalizes the steps necessary for compliance. (3) Generating Clinical Trial Designs: To streamline the development of study designs, our platform displays clinical trials for devices that are semantically similar to yours, sourced from the clinicaltrials.gov database. We then generate potential designs, incorporating inclusion/exclusion criteria, intervention modules (control and experimental groups), study procedures, and outcome measures, based on a comprehensive analysis of clinical trial data. This refined approach not only clarifies the path to FDA approval but also positions our platform as a pivotal tool in bringing medical devices to market more efficiently and cost-effectively.

**How we built it**

Our application consists of a Next.Js frontend and Flask backend. For generating our dataset of all the 510k summaries, we developed our own custom pdf extractor to extract different sections and tables from the 510K summary documents on the FDA site. In order to find the best predicate device we developed a matrix similarity algorithm using the vector similarity search built off of our data storage in Pinecone. Then we used openai GPT-4-turbo to generate comparison tables among the two devices which is a section of the 510k document. To visualize the database (in order for users to see where their device lies in the space of all similar devices), we used Nomic’s Atlas module. Lastly, we automatically collected a dataset of clinical trial designs from clinicaltrials.gov and used Together API to finetune a collection of clinical trials generation models and also used openAI’s LLM for trial generation.

**Challenges we ran into**

Table extraction: Each PDF document for 510k summaries has a different format (some are scanned copies and some are pdfs with differently formatted tables and various headers that aren’t standard). We tried multiple approaches using computer vision, ocr, table extraction libraries, and heuristics and ultimately creating our own custom approach that ran faster than the other approaches in order to populate our database with all of the previous records of 510ks. Deployment: Due to some dependencies, we had an issue with deploying the full application so we could deploy the frontend and backend separately.

**Accomplishments that we're proud of**

We’re really proud of actually solving a use case that we’ve seen a huge need for first hand and no real solution. We truly believe that cost, time, or ambiguity should not be the reason that a medical device which can transform a person’s life doesn’t get to market soon enough.

**What we learned**
- Prioritization of features and shipping an MVP that addresses the most impactful ones
- Table extraction among pdfs isn’t a solved task and there are many nuances
- The FDA process is very complex

**What's next for FastTrackFDA**

Our overall mission is to be a central platform for all device companies to start using when they begin conception of a device.

**Future steps that we plan to build out soon are:**
- Expanding to other areas of the FDA process such as quality Assurance: 75% of the time that a consultant spends is revising existing drafted material and making sure it abides by the Refusal to Accept checklist documentation and guidance documents for each section. We plan to user our table extraction tool to extract this RTA document and do automatic checking before the person creates the table.
- Improve table extraction to be more generalizable: We hope users can ask interactive questions about guidance documents but only once they’ve been parsed perfectly.
- Continuous learning and multi-agent reviewal- Since newer FDA device approvals are most important, we plan to integrate a continuous learning approach that constantly updates the data as soon as a new device is approved.
- Multi-agent reviewal: For each part of this workflow, we hope to embed more specific knowledge about the decision making process of a FDA consultant. For example, for determining clinical trial design we hope to embed an “clinical trial expert” agent that checks whether the trial’s inclusion/ exclusion reduces bias, its trial design is safe, and potential suggestions.

**Built With**
- flask
- next.js
- openai
- pinecone
- sentence-transformers
- tailwind
- together
- nomic

**Try It Out**

Please run `FLASK_DEBUG=1 pip3 install -r ../backend/requirements.txt && python3 -m flask --app ../backend/index run -p 5328` in one terminal.

In another, please run `npm install && npm run dev`


## Detected evidence (automated analysis)

Indexed codebase: 37 recognized source files, 302 KB.
- CSS (language) — detected in the code
- Flask (technology) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- OpenAI (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (42 of 42)

```
.gitignore
backend/clinical_trials_vector_db.py
backend/create_local_dataset.py
backend/create_vector_db.py
backend/data/k_numbers.csv
backend/helper_code/chatgpt.py
backend/helper_code/clinical_trials.py
backend/helper_code/delete_metadata.py
backend/helper_code/extract_510k.py
backend/helper_code/extract.py
backend/helper_code/find_predicates.py
backend/helper_code/find_similar_clinical_trial.py
backend/helper_code/finetune_clinicals.py
backend/helper_code/generate_my_trial.py
backend/helper_code/save_k_numbers.py
backend/helper_code/visualization.py
backend/helper_code/workflow.py
backend/index.py
backend/requirements.txt
frontend/.gitignore
frontend/app/dashboard/Chatbox.jsx
frontend/app/dashboard/ClinicalTrials.jsx
frontend/app/dashboard/ComparisonTable.jsx
frontend/app/dashboard/IntroForm.tsx
frontend/app/dashboard/page.tsx
frontend/app/dashboard/PredicateVisualization.jsx
frontend/app/dashboard/Table.jsx
frontend/app/dashboard/TrialVisualization.jsx
frontend/app/dashboard/Workflow.jsx
frontend/app/globals.css
frontend/app/layout.tsx
frontend/app/page.tsx
frontend/app/PrelineScript.tsx
frontend/components/Form.js
frontend/components/Gradient.js
frontend/components/workflowData.js
frontend/next.config.js
frontend/package.json
frontend/postcss.config.js
frontend/tailwind.config.js
frontend/tsconfig.json
README.md
```

### Dependencies

- backend/requirements.txt: Flask@==2.2.2, flask-cors, Gunicorn, nomic, numpy@==1.24.3, openai, pandas@==2.2.0, pillow, pinecone-client, PyMuPDF, python-dotenv, sentence_transformers, together, urllib3@==1.26.18, Werkzeug@==2.2.2
- frontend/package.json: @preline/tooltip@^2.0.2, @tailwindcss/forms@^0.5.7, @types/node@20.1.4, @types/react@18.2.6, @types/react-dom@18.2.4, autoprefixer@10.4.14, concurrently@^8.0.1, eslint@8.40.0, eslint-config-next@13.4.2, framer-motion@^11.0.5, next@13.4.3, postcss@8.4.23, preline@^2.0.3, react@18.2.0, react-dom@18.2.0, tailwindcss@3.3.2, typescript@5.0.4

### Recent commits (newest first)

- Update README.md
- Update README.md
- change api
- Update README.md
- to deploy to vercel
- ngrok again
- fix minor UI issues
- trail to trial
- parallelize 3.5 calls
- make ui prettier
- bad version
- ngrok url
- buttons for comparison and table
- ngrok
- page
- more landing
- increase time
- landing
- routes
- working clinical trials plus prettier form

## Key source files (fetched from GitHub, selected and truncated for size)

### backend/requirements.txt

```
urllib3==1.26.18
numpy==1.24.3
Flask==2.2.2
Werkzeug==2.2.2
pandas==2.2.0
python-dotenv
pillow
PyMuPDF
sentence_transformers
pinecone-client
nomic
Gunicorn
flask-cors
together
openai
```

### frontend/package.json

```
{
  "name": "next-flask",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "flask-dev": "FLASK_DEBUG=1 pip3 install -r ../backend/requirements.txt && python3 -m flask --app ../backend/index run -p 5328",
    "flask": "FLASK_DEBUG=1 pip3 install -r ../backend/requirements.txt && python3 -m flask --app ../backend/index run -p 5328",
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@preline/tooltip": "^2.0.2",
    "@types/node": "20.1.4",
    "@types/react": "18.2.6",
    "@types/react-dom": "18.2.4",
    "autoprefixer": "10.4.14",
    "concurrently": "^8.0.1",
    "eslint": "8.40.0",
    "eslint-config-next": "13.4.2",
    "framer-motion": "^11.0.5",
    "next": "13.4.3",
    "postcss": "8.4.23",
    "preline": "^2.0.3",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "tailwindcss": "3.3.2",
    "typescript": "5.0.4"
  },
  "devDependencies": {
    "@tailwindcss/forms": "^0.5.7"
  }
}

```

### backend/index.py

```python
from dotenv import load_dotenv
load_dotenv()
from helper_code.finetune_clinicals import generate_eligibility
from helper_code.generate_my_trial import generate_my_clinical_trial, generate_my_clinical_trial_35
from helper_code.find_similar_clinical_trial import get_all_similar_trials
from helper_code.visualization import visualize
from helper_code.find_predicates import get_final_comparison_table, parallel_process, predicates
from flask import Flask, render_template_string, request, redirect, url_for, jsonify
from flask_cors import CORS, cross_origin
import nomic
import os

app = Flask(__name__)
CORS(app)
# CORS(app, resources={r"/api/*": {"origins": "https://tree-hacks-fda-approval-n8ognjakq-treehacks.vercel.app"}})

nomic.login(os.getenv("NOMIC_API_KEY"))

@app.route("/api/home")
@cross_origin()
def home():
    return "Hello, hello!"

@app.route("/api/python")
@cross_origin()
def go_to_dashboard():
    return redirect('/dashboard')

@app.route("/api/handle-form", methods=['POST'])
@cross_origin()
def handle_form():
    # Retrieve form data
    device_description = request.form['device-description']
    indication_for_use = request.form['use-indication']
    k_number_description = request.form.get('k-number-description', None) #{
    k_number_use = request.form.get('k-number-use', None) #{
    k_number = request.form.get('k-number', None) #{
         #   "K": k_number,
          #  "Device Description": device_description,
           # "Indication for Use": indication_for_use,
        # }
    
    # Process the form data (for demonstration, print it to console)
    print(f"Device Description: {device_description}")
    print(f"Indication for Use: {indication_for_use}")

    device_data = {
        "Device Description": device_description,
        "Indication for Use": indication_for_use
    }
    # all_information = []
    # if not k_number:
    #     all_information = predicates(device_data)
    #     # if len(all_information) > 1:
    #     k_number_information = all_information[:1]
    # else:
    #     k_number_information = [{
    #         "K": k_number,
    #         "Device Description": k_number_description,
    #         "Indication for Use": k_number_use
    #     }]
    # print("---------------- K number information: \n\n\n", str(k_number_information))
    
    # list_comparisons = parallel_process(k_number_information, device_description, indication_for_use)
    return {
        "k_number_information": [{
            "K": "K123456",
            "Device Description": "Device 1",
            "Indication for Use": "Use 1"
        }],
        "comparison_table": [
            [
                ["", "Header 1", "Header 2"],
                ["Row 1", "In the heart of the forest, a stream whispers its secrets to the wind, while sunlight filters through the leaves, painting patterns on the forest floor in dappled shades of green.", "The old oak tree stood tall, its branches reaching out like arms embracing the sky."],
                ["Row 2", "Lost in the labyrinth of city streets, strangers pass like ships in the night, each with a story untold, each with a destination yet to be discovered.", "Amidst the chaos of the bustling marketplace, a street performer captivates the crowd with his mesmerizing melodies, transporting them to a world of magic and wonder."],
                ["Row 3", "Beneath the starlit sky, waves crash against the rugged cliffs, their thunderous applause echoing through the silent night.", "The salty breeze carries whispers of tales from distant lands, mingling with the sounds of seagulls crying out over the restless sea."],
                ["Row 4", "Through the window, the first light of dawn paints the room in hues of gold and pink, awakening the world to a new day.", "In the cozy warmth of the kitchen, the aroma of freshly baked bread fills the air, evoking memories of simpler times."],
                ["Row 5", "Nestled in the embrace of rolling hills, a quaint village sleeps soundly under the stars, its secrets hidden within the whispers of the night.", "A solitary figure stands atop the mountain, gazing out over the vast expanse below, finding solace in the quiet majesty of nature."]
            ]
        ]
    }

    # return {
    #     "k_number_information": all_information,
    #     "comparison_table": list_comparisons
    # }

@app.route("/api/visualize-predicate", methods=['POST'])
@cross_origin()
def get_visualization_predicate():
    device_description = request.form['device-description']
    indication_for_use = request.form['use-indication']
    user_data = {
        "Device Description": device_description,
        "Indication for Use": indication_for_use
    }

    url = visualize(user_data, "ns1", "k_number", "Visualize the closest devices to your device")

    return {
        "url": url
    }

@app.route("/api/visualize-trials", methods=['POST'])
@cross_origin()
def get_visualization_trials():
    device_description = request.form['device-description']
    indication_for_use = request.form['use-indication']
    user_data = {
        "Device Description": device_description,
        "Indication for Use": indication_for_use
    }

    url = visualize(user_data, "ns2", "nct_code", "Visualize the closest clinical trials to your expected trial")

    return {
        "url": url
    }

@app.route("/api/similar-trials", methods=['POST'])
@cross_origin()
def similar_trials():
    device_description = request.form['device-description']
    all_trials = get_all_similar_trials(device_description)

    return {
        "all-trials": all_trials
    }

@app.route("/api/generate-trial", methods=['POST'])
@cross_origin()
def generate_trial():
    device_description = request.form['device-description']
    indications_use = request.form['use-indication']
    all_trials = request.form['all-trials']
    eligibility = generate_eligibility(device_description)
    trial_info = generate_my_clinical_trial_35(all_trials, device_description, indications_use, eli
[truncated — 63 more characters]
```

### frontend/app/layout.tsx

```typescript
import './globals.css'
import { Inter } from 'next/font/google'
import PrelineScript from './PrelineScript'

const inter = Inter({ subsets: ['latin'] })

export const metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className={inter.className + " bg-neutral-100 text-gray-700"} id="body">
        {children}
      </body>
      <PrelineScript />
    </html>
  )
}

```

### frontend/app/page.tsx

```typescript
"use client";

import Link from "next/link";
import { AnimatePresence, motion } from "framer-motion";
import { gradient } from "@/components/Gradient";
import { useEffect } from "react";

export default function Home() {
  useEffect(() => {
    gradient.initGradient("#gradient-canvas");
  }, []);

  return (
    <AnimatePresence>
      <div className="min-h-[100vh] sm:min-h-screen w-screen flex flex-col relative bg-[#F2F3F5] font-inter overflow-hidden">
        <svg
          style={{ filter: "contrast(125%) brightness(110%)" }}
          className="fixed z-[1] w-full h-full opacity-[35%]"
        >
          <filter id="noise">
            <feTurbulence
              type="fractalNoise"
              baseFrequency=".7"
              numOctaves="3"
              stitchTiles="stitch"
            ></feTurbulence>
            <feColorMatrix type="saturate" values="0"></feColorMatrix>
          </filter>
          <rect width="100%" height="100%" filter="url(#noise)"></rect>
        </svg>
        <main className="flex flex-col justify-center h-[90%] static md:fixed w-screen overflow-hidden grid-rows-[1fr_repeat(3,auto)_1fr] z-[100] pt-[30px] pb-[320px] px-4 md:px-20 md:py-0">
          <motion.svg
            initial={{ opacity: 0, y: 40 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{
              delay: 0.15,
              duration: 0.95,
              ease: [0.165, 0.84, 0.44, 1],
            }}
            className="block w-[100px] row-start-2 mb-8 md:mb-6"
            viewBox="0 0 87 15"
            fill="none"
            xmlns="http://www.w3.org/2000/svg"
          >
            <path
              d="M0.432617 14H9.31836V11.0469H4.15918V1.31738H0.432617V14ZM13.3771 14H17.1037V1.31738H13.3771V14ZM21.7689 14H25.4955V9.50879H30.1889V6.71387H25.4955V4.27051H30.6811V1.31738H21.7689V14ZM37.5875 14H41.3141V4.27051H44.8297V1.31738H34.0719V4.27051H37.5875V14ZM54.241 14.3428C55.5359 14.3428 56.6609 14.0732 57.616 13.5342C58.5711 12.9951 59.3094 12.2275 59.8309 11.2314C60.3582 10.2354 60.6219 9.0459 60.6219 7.66309V7.64551C60.6219 6.26855 60.3582 5.08203 59.8309 4.08594C59.3094 3.08984 58.5711 2.32227 57.616 1.7832C56.6609 1.24414 55.5359 0.974609 54.241 0.974609C52.952 0.974609 51.827 1.24414 50.866 1.7832C49.9109 2.31641 49.1697 3.08398 48.6424 4.08594C48.115 5.08203 47.8514 6.26855 47.8514 7.64551V7.66309C47.8514 9.0459 48.1121 10.2383 48.6336 11.2402C49.1609 12.2363 49.9021 13.0039 50.8572 13.543C51.8182 14.0762 52.9461 14.3428 54.241 14.3428ZM54.241 11.3018C53.7313 11.3018 53.2801 11.1553 52.8875 10.8623C52.5008 10.5635 52.1961 10.1416 51.9734 9.59668C51.7566 9.0459 51.6482 8.40137 51.6482 7.66309V7.64551C51.6482 6.90723 51.7566 6.26562 51.9734 5.7207C52.1961 5.17578 52.5008 4.75684 52.8875 4.46387C53.2801 4.16504 53.7313 4.01562 54.241 4.01562C54.7508 4.01562 55.199 4.16504 55.5857 4.46387C55.9783 4.75684 56.283 5.17578 56.4998 5.7207C56.7166 6.26562 56.825 6.90723 56.825 7.64551V7.66309C56.825 8.40137 56.7166 9.0459 56.4998 9.59668C56.283 10.1416 55.9783 10.5635 55.5857 10.8623C55.199 11.1553 54.7508 11.3018 54.241 11.3018ZM64.6631 14H68.3896V9.50879H73.083V6.71387H68.3896V4.27051H73.5752V1.31738H64.6631V14ZM77.6252 14H81.3518V9.50879H86.0451V6.71387H81.3518V4.27051H86.5373V1.31738H77.6252V14Z"
              fill="#1E2B3A"
            />
          </motion.svg>

          <motion.h1
            initial={{ opacity: 0, y: 40 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{
              delay: 0.15,
              duration: 0.95,
              ease: [0.165, 0.84, 0.44, 1],
            }}
            className="relative md:ml-[-10px] md:mb-[37px] font-extrabold text-[16vw] md:text-[130px] font-inter text-[#1E2B3A] leading-[0.9] tracking-[-2px] z-[100]"
          >
            FastTrackFDA <br />
            <span className="font-inter text-[#407BBF]">.</span>
          </motion.h1>
          <motion.div
            initial={{ opacity: 0, y: 40 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{
              delay: 0.15,
              duration: 0.95,
              ease: [0.165, 0.84, 0.44, 1],
            }}
            className="flex flex-row justify-center z-20 mx-0 mb-0 mt-8 md:mt-0 md:mb-[35px] max-w-2xl md:space-x-8"
          >
            <div className="w-1/2">
              <h2 className="flex items-center font-semibold text-[1em] text-[#1a2b3b]">
                Accelerating Med Device Innovations from Concept to Market
              </h2>
              <p className="text-[14px] leading-[20px] text-[#1a2b3b] font-normal">
                
              </p>
            </div>
            <div className="w-1/2">
              <h2 className="flex items-center font-semibold text-[1em] text-[#1a2b3b]">
                
              </h2>
              <p className="text-[14px] leading-[20px] text-[#1a2b3b] font-normal">
                
              </p>
            </div>
          </motion.div>

          <div className="flex gap-[15px] mt-8 md:mt-0">
            <motion.div
              initial={{ opacity: 0, y: 40 }}
              animate={{ opacity: 1, y: 0 }}
              transition={{
                delay: 0.55,
                duration: 0.55,
                ease: [0.075, 0.82, 0.965, 1],
              }}
            >
              <Link
                href="https://github.com/Tameyer41/liftoff"
                target="_blank"
                className="group rounded-full pl-[8px] min-w-[180px] pr-4 py-2 text-[13px] font-semibold transition-all flex items-center justify-center bg-[#1E2B3A] text-white hover:[linear-gradient(0deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.1)), #0D2247] no-underline flex gap-x-2  active:scale-95 scale-100 duration-75"
                style={{
                  boxShadow:
                    "0px 1px 4px rgba(13, 34, 71, 0.17), inset 0px 0px 0px 1px #061530, inset 0px 0px 0px 2px rgba(255, 255, 255, 0.1)",
                }}
              >
                <span clas
[truncated — 3516 more characters]
```

### frontend/app/dashboard/page.tsx

```typescript
'use client'

import ComparisonTable from './ComparisonTable'
import IntroForm from './IntroForm'
import Workflow from './Workflow'
import ClinicalTrials from './ClinicalTrials'
import TrialVisualization from './TrialVisualization'
import PredicateVisualization from './PredicateVisualization'
import Table from './Table'
import React from 'react';

enum ScreenTypes {
    InputForm = 'inputForm',
    Results = 'results',
}

enum SubScreenTypes {
    Comparison = 'comparison',
    Workflow = 'workflow',
    PredicateVisualization = 'predicateVisualization',
    TrialVisualization = 'trialVisualization',
    ClinicalTrials = 'clinicalTrials',
    MyTrials = 'myTrials',
}

// const BACKEND_BASE: string = "https://fda-approval-service.onrender.com/";
const BACKEND_BASE: string = "http://localhost:3000/";
// const BACKEND_BASE:string = "http://b245-68-65-175-77.ngrok-free.app/";

export default function PredicateComparison() {
    const [description, setDescription] = React.useState<string>("The Fitbit ECG App is a software-only medical device used to create, record, display, store and analyze a single channel ECG. The Fitbit ECG App consists of a Device application (“Device app”) on a consumer Fitbit wrist-worn product and a mobile application tile (“mobile app”) on Fitbit’s consumer mobile application. The Device app uses data from electrical sensors on a consumer Fitbit wrist-worn product to create and record an ECG. The algorithm on the Device app analyzes a 30 second recording of the ECG and provides results to the user. Users are able to view their past results as well as a pdf report of the waveform similar to a Lead I ECG on the mobile app.");
    const [indication, setIndication] = React.useState<string>("The Fitbit ECG App is a software-only mobile medical application intended for use with Fitbit wrist-wearable devices to create, record, store, transfer, and display a single channel electrocardiogram (ECG) qualitatively similar to a Lead I ECG. The Fitbit ECG App determines the presence of atrial fibrillation (AFib) or sinus rhythm on a classifiable waveform. The AF detection function is not recommended for users with other known arrhythmias. The Fitbit ECG App is intended for over-the-counter (OTC) use. The ECG data displayed by the Fitbit ECG App is intended for informational use only. The user is not intended to interpret or take clinical action based on the device output without consultation of a qualified healthcare professional. The ECG waveform is meant to supplement rhythm classification for the purposes of discriminating AFib from normal sinus rhythm and not intended to replace traditional methods of diagnosis or treatment. The Fitbit ECG App is not intended for use by people under 22 years old.");
    const [screenType, setScreenType] = React.useState(ScreenTypes.InputForm);
    const [subScreenType, setSubScreenType] = React.useState(SubScreenTypes.Comparison);
    const [comparisonData, setComparisonData] = React.useState<Record<string, String[][]>>({});
    const [comparisonOptions, setComparisonOptions] = React.useState<{"K": string, "Device Description": string, "Indications for use": string}[]>([]);
    const [comparisonId, setComparisonId] = React.useState<string>("");
    const [clinicalTrials, setClinicalTrials] = React.useState<String[][][]>();
    const [myTrial, setMyTrial] = React.useState<String[][]>();

    async function handleFormSubmit(e: React.FormEvent<HTMLFormElement>) {
        setScreenType(ScreenTypes.Results);
        setSubScreenType(SubScreenTypes.Comparison);
        e.preventDefault();
        // TODO: Fetch data from /api/html-form and update comparisonData
        let formData = new FormData(e.target as HTMLFormElement);
        // formData.append('k-number-information', '');
        console.log(formData.entries());
        console.log({e});
        console.log(e.target);

        const response = await fetch(`${BACKEND_BASE}api/handle-form`, {
            method: 'POST',
            body: formData,
            mode: "no-cors",
        });
        const data = await response.json();
        console.log({data});
        const k_number: string = data["k_number_information"][0]["K"];
        const info: Record<string, {"K": string, "Device Description": string, "Indications for use": string}> = {};
        for (const k of data["k_number_information"]) {
            info[k["K"]] = k;
        }
        const table: Record<string, string[][]> = {};
        table[k_number] = data["comparison_table"][0];
        
        setComparisonData(table);
        setComparisonOptions(data["k_number_information"]);
        setComparisonId(k_number);
        console.log({data});
        console.log({table});
        console.log({k_number});
    }

    async function handleClinicalTrials() {
        setSubScreenType(SubScreenTypes.ClinicalTrials);
        let formData = new FormData();
        formData.append('device-description', description);
        const response = await fetch(`${BACKEND_BASE}api/similar-trials`, {
            method: 'POST',
            body: formData,
            mode: "no-cors"
        });
        const data = await response.json();
        console.log({data});
        const trials = data["all-trials"];
        console.log({trials});
        setClinicalTrials(trials);
        console.log({clinicalTrials});
    }

    async function handleChangeComparison(k_number: string) {
        if (!(k_number in comparisonData)) {
            // const form: HTMLFormElement = document.getElementById('comparison-form') as HTMLFormElement;
            let formData = new FormData();
            formData.append('device-description', description);
            formData.append('use-indication', indication);
            comparisonOptions.forEach((option) => {
                if (option["K"] === k_number) {
                    // formData.append('k-number-information', String(option));
                    console.log(option)
                    console.log(option
[truncated — 10683 more characters]
```

### frontend/postcss.config.js

```javascript
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

```

### frontend/next.config.js

```javascript
/** @type {import('next').NextConfig} */
const nextConfig = {
  rewrites: async () => {
    return [
      {
        source: '/api/:path*',
        destination:
          process.env.NODE_ENV === 'development'
            ? 'http://127.0.0.1:5328/api/:path*'
            : '/api/',
      },
    ]
  },
  httpAgentOptions: {
    keepAlive: false,
  },
  staticPageGenerationTimeout: 120,
}

module.exports = nextConfig

```

### frontend/tailwind.config.js

```javascript
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './pages/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
    './app/**/*.{js,ts,jsx,tsx,mdx}',
    'node_modules/preline/dist/*.js',
    './node_modules/preline/preline.js',
  ],
  theme: {
    extend: {
      backgroundImage: {
        'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
        'gradient-conic':
          'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
      },
    },
  },
  plugins: [
    require('preline/plugin'),
  ],
  darkMode: 'class',
}

```

### backend/create_local_dataset.py

```python
from helper_code.extract_510k import get_k_numbers, get_pdf_link
from helper_code.extract import download_pdf_to_file
import pandas as pd
import os
import time 
import random
import csv

def load_pd_frame(csv_filename):

    # Check if the CSV file exists
    if os.path.exists(csv_filename):
        # Load the DataFrame from the CSV file
        df = pd.read_csv(csv_filename)
        print(f"Loaded DataFrame from {csv_filename}")
    else:
        # Initialize an empty DataFrame with the desired columns if CSV doesn't exist
        df_columns = ["k_number", "url", "download_pdf_to_file", "product_code", "nct_code", "pdf_filename"]
        df = pd.DataFrame(columns=df_columns)
        save_df(df, csv_filename)
        print("Initialized new DataFrame")

    return df

def get_dataset(k_numbers, df, start_index, end_index, csv_filename, indices_visited_filename):
    for i in range(start_index, end_index):
        k_number = k_numbers[i]
        print("k num", k_number)
        print("index", i)
        
        info = get_pdf_link(k_number)
        random_integer = random.randint(5, 15)
        time.sleep(random_integer)

        if info is None:
            print("no info found")
        elif "summary_url" in info:
            pdf_filename = "./pdfs/" + k_number[0] + ".pdf"
            pdf_file = download_pdf_to_file(info["summary_url"], pdf_filename)
            random_integer = random.randint(5, 15)
            time.sleep(random_integer)

            if pdf_file:
                # Add the information to the DataFrame
                info_to_insert = info.copy()  # Make a copy to avoid modifying the original
                info_to_insert["pdf_filename"] = pdf_filename  # Add the PDF filename to the info
                df = pd.concat([df, pd.DataFrame([info_to_insert])], ignore_index=True)
                save_df(df, csv_filename)
                print("GOT IT")
            else:
                print("download failed - pdf file not found")
        else:
            print("no summary url")
        
        # Open the CSV file in append mode ('a') so we can add to it without overwriting existing content
        with open(indices_visited_filename, mode='a', newline='') as file:
            writer = csv.writer(file)
            
            # Write the new row to the CSV file
            writer.writerow([str(i)])
    return df

def save_df(df, csv_filename):
    df.to_csv(csv_filename, index=False)
    print(f"DataFrame saved to {csv_filename}")


if __name__ == "__main__":

    #MAKE SURE TO CREATE A /pdfs FOLDER IN THE DIRECTORY
    start = 0 #FILL IN
    end = 0 #FILL IN
    start = 6000
    end = 10000

    csv_filename = "./data/pdf_dataset.csv"

    df = load_pd_frame(csv_filename)

    k_filename = "./data/k_numbers.csv"
    k_numbers = get_k_numbers(k_filename)

    indices_visited_filename = "index_done.csv"

    df = get_dataset(k_numbers, df, start, end, csv_filename, indices_visited_filename)

    # Save the DataFrame to a CSV file
    save_df(df, csv_filename)
```

[26 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]