Skip to content

Commit

Permalink
Merge pull request #12 from rajatkriplani/feature/migrate-html-to-nextjs
Browse files Browse the repository at this point in the history
Feature/migrate html to nextjs
  • Loading branch information
Namit2111 authored Oct 8, 2024
2 parents e482a41 + fb1eb46 commit 7ab4b07
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 31 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,20 @@ Welcome to the **Bible Verse Finder**! This repository contains code to parse an

3. Enter a theme to find similar Bible verses!

### Installing the frontend (Next.Js)
1. Change directory to the `bible-app` folder
```sh
cd bible-app
```

2. Run `npm install` inside the folder

3. Navigate to `http://localhost:3000`

4. Start the server as instructed above

5. Enter a theme to find similar Bible verses!

__OR__

[TRY IT OUT! ⭐✝️](https://bible-verse-finder-1.onrender.com/)
Expand Down
61 changes: 40 additions & 21 deletions app.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from flask import Flask, request, render_template
from flask import Flask, request, render_template, jsonify
import pickle
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
Expand Down Expand Up @@ -27,36 +27,55 @@
# Predict the clusters for the John verses
labels_loaded = loaded_km.predict(X_loaded)

@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
user_input = request.form['user_input']
def get_similar_verses(user_input):
# Transform the user input using the loaded TF-IDF vectorizer
user_input_vector = loaded_vectorizer.transform([user_input])

# Transform the user input using the loaded TF-IDF vectorizer
user_input_vector = loaded_vectorizer.transform([user_input])
# Predict the cluster for the user input
predicted_cluster = loaded_km.predict(user_input_vector)[0]

# Predict the cluster for the user input
predicted_cluster = loaded_km.predict(user_input_vector)[0]
# Get the indices of the verses in the predicted cluster
cluster_indices = np.where(labels_loaded == predicted_cluster)[0]

# Get the indices of the verses in the predicted cluster
cluster_indices = np.where(labels_loaded == predicted_cluster)[0]
# Get the TF-IDF vectors for the verses in the predicted cluster
cluster_verses = [John[i] for i in cluster_indices]
cluster_vectors = X_loaded[cluster_indices]

# Get the TF-IDF vectors for the verses in the predicted cluster
cluster_verses = [John[i] for i in cluster_indices]
cluster_vectors = X_loaded[cluster_indices]
# Compute similarity between the user input and each verse in the cluster
similarities = cosine_similarity(user_input_vector, cluster_vectors).flatten()

# Compute similarity between the user input and each verse in the cluster
similarities = cosine_similarity(user_input_vector, cluster_vectors).flatten()
# Get the indices of the top 5 most similar verses
top_indices = similarities.argsort()[-5:][::-1]

# Get the indices of the top 5 most similar verses
top_indices = similarities.argsort()[-5:][::-1]
# Prepare the results
results = [(cluster_verses[i], similarities[i]) for i in top_indices]

return results

# Prepare the results
results = [(cluster_verses[i], similarities[i]) for i in top_indices]
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
user_input = request.form['user_input']
results = get_similar_verses(user_input)

return render_template('index.html', results=results, user_input=user_input)

return render_template('index.html', results=None)

@app.route('/api/similarity', methods=['POST'])
def similarity():
data = request.json
user_input = data.get('user_input')

if not user_input:
return jsonify({"error": "No input provided"}), 400

results = get_similar_verses(user_input)

return jsonify({
"user_input": user_input,
"results": results
})

if __name__ == '__main__':
app.run(debug=False,host='0.0.0.0')
app.run(debug=False, host='0.0.0.0')
24 changes: 24 additions & 0 deletions bible-app/app/api/similarity/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { NextResponse } from 'next/server';

export async function POST(request: Request) {
const { userInput } = await request.json();
try {
// Make a request to the Flask app
const response = await fetch('http://127.0.0.1:5000/api/similarity', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ user_input: userInput }),
});

if (!response.ok) {
throw new Error('Failed to fetch data');
}

const data = await response.json();
return NextResponse.json(data);
} catch (error) {
return NextResponse.json({ error: 'An error occurred while fetching data' }, { status: 500 });
}
}
89 changes: 89 additions & 0 deletions bible-app/app/components/VerseSearch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"use client";

import { useState } from "react";
import { VerseSimilarity } from "@/lib/interface";

export default function VerseSearch() {
const [userInput, setUserInput] = useState("");
const [verses, setVerses] = useState<VerseSimilarity>();
const [error, setError] = useState<string>();

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setUserInput(e.target.value);
setError("");
}

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
const response = await fetch('/api/similarity', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ userInput }),
});

if (!response.ok) {
setError('Failed to fetch data');
return
}

const data = await response.json();
setVerses(data);
setUserInput("");
setError("");
} catch (error) {
setError('An error occurred while fetching data');
}
};

const filteredVerses = verses && verses.results.filter((result) => result[1] > 0);

return (
<div className="flex flex-col min-h-screen items-center justify-center p-8 gap-8 max-sm:m-auto">
<h1 className="text-2xl sm:text-4xl font-extrabold sm:font-bold text-center text-siteColor">
Bible Verse Similarity
</h1>
<form onSubmit={handleSubmit} className="flex flex-col items-center gap-2 bg-white p-8 rounded-xl shadow-xl w-full sm:w-[32rem]">
<p className="text-xl font-bold text-gray-700">Enter a theme:</p>
<input
type="text"
value={userInput}
onChange={(e) => handleInputChange(e)}
required
className="p-2 border border-gray-300 rounded w-full mb-8 text-lg"
/>
{error && <p className="text-red-500 mt-[-6px]">{error}</p>}
<button
type="submit"
className="bg-siteColor/95 text-white py-3 px-6 w-full rounded mt-4 hover:bg-siteColor text-center text-lg max-xs:text-sm font-semibold">
Find Similar Verses
</button>
</form>
{/* Display results if any are available */}
{verses && (
<div className="flex flex-col items-center gap-4">
<h2 className="text-xl sm:text-3xl font-bold text-gray-700">Results for "{verses.user_input}"</h2>
{filteredVerses?.length === 0 ? (
<p className="text-sm sm:text-xl font-bold text-gray-600 text-center">
Your search returned void 😅, good news God's word never does! Try another search 🔎
</p>
) :
<ul className="flex flex-col gap-4 w-full sm:w-[80%] text-gray-600">
{filteredVerses?.map((result, index) => (
<li
key={index}
className="p-4 bg-white rounded-xl shadow">
<p className="text-md font-semibold mb-2"><span className="text-siteColor font-bold">Verse:</span> 1 John {result[0]}</p>
<p className="text-md font-semibold"><span className="text-siteColor font-bold">Similarity:</span> {result[1]}</p>
</li>
))
}
</ul>
}
</div>
)}
</div>
);
}
6 changes: 3 additions & 3 deletions bible-app/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ const geistMono = localFont({
});

export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
title: "Bible Verse Similarity",
description: "A tool to find similar verses in the Bible",
};

export default function RootLayout({
Expand All @@ -26,7 +26,7 @@ export default function RootLayout({
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
className={`${geistSans.variable} ${geistMono.variable} antialiased bg-siteBackground`}
>
{children}
</body>
Expand Down
14 changes: 7 additions & 7 deletions bible-app/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import VerseSearch from "./components/VerseSearch";

export default function Home() {
return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
Home page

</div>
);
}
return (
<div className="min-h-screen">
<VerseSearch />
</div>
);
}
4 changes: 4 additions & 0 deletions bible-app/lib/interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface VerseSimilarity {
user_input: string;
results: [string, number][];
}
7 changes: 7 additions & 0 deletions bible-app/tailwind.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Config } from "tailwindcss"
const defaultTheme = require("tailwindcss/defaultTheme");

const config = {
darkMode: ["class"],
Expand All @@ -10,6 +11,10 @@ const config = {
],
prefix: "",
theme: {
screens: {
xs: "336px",
...defaultTheme.screens,
},
container: {
center: true,
padding: "2rem",
Expand All @@ -19,6 +24,8 @@ const config = {
},
extend: {
colors: {
siteColor: "#4CAF50",
siteBackground: "#f5f5f5",
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
Expand Down
2 changes: 2 additions & 0 deletions contributors.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
- namit2111
- justinhSE
- lisamessier
- rajatkriplani
- d-beloved

0 comments on commit 7ab4b07

Please sign in to comment.