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)
if semester_id:
queryset = queryset.filter(semester__id=semester_id)
queryset = queryset.annotate(
upvote_count=Count("upvotes"), downvote_count=Count("downvotes")

View File

@@ -15,14 +15,24 @@ CORS(app)
@app.route("/copy-file", methods=["POST"])
def copy_file():
try:
data = request.json
source_file = data["file_path"]
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)
files = request.files
id = request.form["file_id"]
print(id)
if "file" not in files:
return "File must be sent in request", 400
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)
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
except Exception as 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():
data = request.json
file_id = data["id"]
@@ -64,7 +74,7 @@ def request_file():
url = f"http://{ip}:8080/send"
params = {
"id": file_id,
"filename": filename,
"filename": filename.replace(" ", "_"),
}
response = requests.get(url, params=params)
@@ -83,6 +93,13 @@ def request_file():
print("Error:", error)
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"])
def request_test_():

View File

@@ -3,11 +3,12 @@ import { Link, useNavigate } from 'react-router-dom';
import { Outlet } from "react-router-dom";
import { SessionContext, destroySessionCookie, getSessionCookie } from "./contexts/session";
import { useState, useEffect, useContext } from "react";
import { usePoll } from "./hooks/usePoll";
export default function App() {
const [session, setSession] = useState(getSessionCookie());
const navigate = useNavigate();
usePoll();
// Redirect to login if session is undefined
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 { getAuthHeaders } from '../utils/getAuthHeaders';
import { Professor, Course, Topic, Semester } from "../types/types";
import { getSessionCookie } from '../contexts/session';
interface Filters extends Record<string, string> {
professor: string;
@@ -23,6 +24,9 @@ const MainSearch: React.FC = () => {
const navigate = useNavigate();
useEffect(() => {
if (!getSessionCookie()) {
return;
}
// Fetch professors
fetchProfessors();
// Fetch courses

View File

@@ -1,29 +1,84 @@
import "../styles/RegisterFile.css";
import { RegisteredFile } from "../types/types";
import { RegisteredFile, Status } from "../types/types";
import { useState, useEffect } from "react";
import { getAuthHeaders } from "../utils/getAuthHeaders";
export default function RegisterFile() {
const [serverFiles, setServerFiles] = useState<RegisteredFile[]>([]);
const [registeredFiles, setRegisteredFiles] = useState<RegisteredFile[]>([]);
useEffect(() => {
fetchRegisteredFiles();
fetchServerFiles();
}, [])
const fetchRegisteredFiles = () => {
return
fetch("http://localhost:5000/files") // replace with local api endpoint
useEffect(() => {
fetch(`http://localhost:8080/files`, {
method: 'GET',
})
.then(response => response.json())
.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>) => {
event.preventDefault();
// 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 file = formData.get('file');
console.log(file, typeof file)
// register file with central server
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 (
<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 { getAuthHeaders } from '../utils/getAuthHeaders';
import { File } from '../types/types';
import { Link } from 'react-router-dom';
const Results: React.FC = () => {
const [files, setFiles] = useState<File[]>([]);
@@ -72,7 +73,7 @@ const Results: React.FC = () => {
<td>{file.upvotes.length}</td>
<td>{file.downvotes.length}</td>
<td>{file.original_author.username}</td>
<td><a href="">Download</a></td>
<td><DownloadButton file={file} /></td>
</tr>
))}
</tbody>
@@ -86,3 +87,36 @@ const Results: React.FC = () => {
};
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;
justify-content: center; /* If you're centering the content */
height: 100vh; /* Full height */
margin-top: -100px; /* Move up */
margin-top: 100px; /* Move up */
width: 100%;
}

View File

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