Merge pull request #21 from AmanTahiliani/alexa-integration

Frontend + backend integration
This commit is contained in:
Aman Tahiliani
2024-04-22 10:38:28 -04:00
committed by GitHub
9 changed files with 168 additions and 23 deletions

View File

@@ -282,6 +282,7 @@ class FileFilterView(APIView):
queryset = queryset.filter(course__id=course_id) queryset = queryset.filter(course__id=course_id)
if semester_id: if semester_id:
queryset = queryset.filter(semester__id=semester_id) queryset = queryset.filter(semester__id=semester_id)
queryset = queryset.annotate( queryset = queryset.annotate(
upvote_count=Count("upvotes"), downvote_count=Count("downvotes") upvote_count=Count("upvotes"), downvote_count=Count("downvotes")

View File

@@ -15,14 +15,24 @@ CORS(app)
@app.route("/copy-file", methods=["POST"]) @app.route("/copy-file", methods=["POST"])
def copy_file(): def copy_file():
try: try:
data = request.json files = request.files
source_file = data["file_path"] id = request.form["file_id"]
id = data["file_id"] print(id)
if not os.path.exists(source_file): if "file" not in files:
return f"Source file does not exist at {source_file}", 400 return "File must be sent in request", 400
filename = os.path.basename(source_file) file = request.files['file']
filename = file.filename.replace(" ", "_")
# id = data["file_id"]
# if not os.path.exists(source_file):
# return f"Source file does not exist at {source_file}", 400
# filename = os.path.basename(source_file)
destination_path = os.path.join("./uploads/", str(id) + "_" + filename) destination_path = os.path.join("./uploads/", str(id) + "_" + filename)
shutil.copyfile(source_file, destination_path) # shutil.copyfile(source_file, destination_path)
if os.path.exists(destination_path):
return "File with name already exists", 200
with open(destination_path, "wb") as f:
f.write(file.read())
return "File Moved successfully", 200 return "File Moved successfully", 200
except Exception as e: except Exception as e:
print(f"An error occured: {e}") print(f"An error occured: {e}")
@@ -54,7 +64,7 @@ def send():
) )
@app.route("/request", methods=["GET"]) @app.route("/request", methods=["POST"])
def request_file(): def request_file():
data = request.json data = request.json
file_id = data["id"] file_id = data["id"]
@@ -64,7 +74,7 @@ def request_file():
url = f"http://{ip}:8080/send" url = f"http://{ip}:8080/send"
params = { params = {
"id": file_id, "id": file_id,
"filename": filename, "filename": filename.replace(" ", "_"),
} }
response = requests.get(url, params=params) response = requests.get(url, params=params)
@@ -83,6 +93,13 @@ def request_file():
print("Error:", error) print("Error:", error)
return error, response.status_code return error, response.status_code
@app.route("/files", methods=["GET"])
def list_files():
files = os.listdir("./uploads")
newFiles = []
for file in files:
newFiles.append("_".join(file.split("_", 1)[1:]))
return {"files": newFiles}, 200
@app.route("/request-tests", methods=["GET"]) @app.route("/request-tests", methods=["GET"])
def request_test_(): def request_test_():

View File

@@ -3,11 +3,12 @@ import { Link, useNavigate } from 'react-router-dom';
import { Outlet } from "react-router-dom"; import { Outlet } from "react-router-dom";
import { SessionContext, destroySessionCookie, getSessionCookie } from "./contexts/session"; import { SessionContext, destroySessionCookie, getSessionCookie } from "./contexts/session";
import { useState, useEffect, useContext } from "react"; import { useState, useEffect, useContext } from "react";
import { usePoll } from "./hooks/usePoll";
export default function App() { export default function App() {
const [session, setSession] = useState(getSessionCookie()); const [session, setSession] = useState(getSessionCookie());
const navigate = useNavigate(); const navigate = useNavigate();
usePoll();
// Redirect to login if session is undefined // Redirect to login if session is undefined
useEffect( useEffect(
() => { () => {

View File

@@ -0,0 +1,33 @@
import { useEffect } from "react"
import { getAuthHeaders } from "../utils/getAuthHeaders"
export const usePoll = () => {
useEffect(() => {
const interval = setInterval(() => {
pollServer()
.catch((error) => {
console.error('Error:', error);
});
}, 30 * 60000) // poll every 30 minutes
return () => {
// clean up
clearInterval(interval);
};
}, [])
const pollServer = async () => {
// request private ip from local api
return fetch('http://localhost:8080/ip')
.then(response => response.text())
.then((data) => {
// send private ip to central server
return fetch('http://localhost:8000/api/poll/', {
method: 'POST',
headers: getAuthHeaders(),
body: JSON.stringify({ip: data}),
})
}).then(response => response.json())
.then(data => {
console.log("Poll response data:", data);
})
}
}

View File

@@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom';
import '../styles/MainScreenWrapper.css'; import '../styles/MainScreenWrapper.css';
import { getAuthHeaders } from '../utils/getAuthHeaders'; import { getAuthHeaders } from '../utils/getAuthHeaders';
import { Professor, Course, Topic, Semester } from "../types/types"; import { Professor, Course, Topic, Semester } from "../types/types";
import { getSessionCookie } from '../contexts/session';
interface Filters extends Record<string, string> { interface Filters extends Record<string, string> {
professor: string; professor: string;
@@ -23,6 +24,9 @@ const MainSearch: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
useEffect(() => { useEffect(() => {
if (!getSessionCookie()) {
return;
}
// Fetch professors // Fetch professors
fetchProfessors(); fetchProfessors();
// Fetch courses // Fetch courses

View File

@@ -1,29 +1,84 @@
import "../styles/RegisterFile.css"; import "../styles/RegisterFile.css";
import { RegisteredFile } from "../types/types"; import { RegisteredFile, Status } from "../types/types";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { getAuthHeaders } from "../utils/getAuthHeaders";
export default function RegisterFile() { export default function RegisterFile() {
const [serverFiles, setServerFiles] = useState<RegisteredFile[]>([]);
const [registeredFiles, setRegisteredFiles] = useState<RegisteredFile[]>([]); const [registeredFiles, setRegisteredFiles] = useState<RegisteredFile[]>([]);
useEffect(() => { useEffect(() => {
fetchRegisteredFiles(); fetchServerFiles();
}, []) }, [])
const fetchRegisteredFiles = () => {
return useEffect(() => {
fetch("http://localhost:5000/files") // replace with local api endpoint fetch(`http://localhost:8080/files`, {
method: 'GET',
})
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
setRegisteredFiles(data); const files: string[] = data.files.map((file: string) => {
return file.replace(/\s/g, "_");
})
setRegisteredFiles(
serverFiles.map((file) => {
if (files.includes(file.filename)) {
file.status = Status.HOSTED;
return file;
}
file.status = Status.PRIVATE;
return file;
})
)
}) })
.catch(error => console.error(error)); }, [serverFiles])
const fetchServerFiles = () => {
fetch(`http://localhost:8000/api/get-peer-files/`, {
method: 'GET',
headers: getAuthHeaders()
})
.then(response => response.json())
.then(data => {
setServerFiles(data)
})
.catch(error => console.error(error));
} }
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => { const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault(); event.preventDefault();
// handle file upload // handle file upload
const fileInput = document.getElementById('file');
// @ts-expect-error - fileInput is an HTMLInputElement
const filename: string = fileInput.files[0].name.replace(/\s/g, "_");
const formData = new FormData(event.target as HTMLFormElement); const formData = new FormData(event.target as HTMLFormElement);
const file = formData.get('file'); // register file with central server
console.log(file, typeof file) fetch(`http://localhost:8000/api/register/`, {
method: 'POST',
headers: getAuthHeaders(),
body: JSON.stringify({
filename: filename,
topic: 1,
semester: 1,
professor: 1,
course: 1,
})
})
.then(response => response.json())
.then(data => {
formData.append("file_id", data.id)
// send file to local
return fetch("http://localhost:8080/copy-file", {
method: 'POST',
body: formData
})
})
.then(response => response.text())
.then(data => {
console.log(data);
window.location.reload();
})
.catch(error => console.error(error));
} }
return ( return (
<div className="container"> <div className="container">

View File

@@ -3,6 +3,7 @@ import { useLocation, useNavigate } from 'react-router-dom';
import styles from '../styles/Results.module.css'; // Make sure this path is correct import styles from '../styles/Results.module.css'; // Make sure this path is correct
import { getAuthHeaders } from '../utils/getAuthHeaders'; import { getAuthHeaders } from '../utils/getAuthHeaders';
import { File } from '../types/types'; import { File } from '../types/types';
import { Link } from 'react-router-dom';
const Results: React.FC = () => { const Results: React.FC = () => {
const [files, setFiles] = useState<File[]>([]); const [files, setFiles] = useState<File[]>([]);
@@ -72,7 +73,7 @@ const Results: React.FC = () => {
<td>{file.upvotes.length}</td> <td>{file.upvotes.length}</td>
<td>{file.downvotes.length}</td> <td>{file.downvotes.length}</td>
<td>{file.original_author.username}</td> <td>{file.original_author.username}</td>
<td><a href="">Download</a></td> <td><DownloadButton file={file} /></td>
</tr> </tr>
))} ))}
</tbody> </tbody>
@@ -86,3 +87,36 @@ const Results: React.FC = () => {
}; };
export default Results; export default Results;
function DownloadButton({ file }: { file: File }) {
const [success, setSuccess] = useState(false);
const handleDownload = () => {
// Implement download functionality
fetch("http://localhost:8080/request", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ id: file.id, filename: file.filename, ip: file.original_author.ip_address }),
})
.then((response) => response.text())
.then((data) => {
console.log("Success download response data:", data);
setSuccess(true);
})
.catch((error) => {
console.error("Error:", error);
setSuccess(false);
});
};
return (
<>
{
success === true ?
<Link to="/register">Downloaded!</Link> :
<button onClick={handleDownload}>Download</button>
}
</>
);
}

View File

@@ -28,7 +28,7 @@ nav div {
align-items: center; align-items: center;
justify-content: center; /* If you're centering the content */ justify-content: center; /* If you're centering the content */
height: 100vh; /* Full height */ height: 100vh; /* Full height */
margin-top: -100px; /* Move up */ margin-top: 100px; /* Move up */
width: 100%; width: 100%;
} }

View File

@@ -38,7 +38,7 @@ export interface File {
downvotes: number[]; downvotes: number[];
} }
enum Status { export enum Status {
HOSTED = "HOSTED", HOSTED = "HOSTED",
PRIVATE = 'PRIVATE', PRIVATE = 'PRIVATE',
} }