Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
from flask_cors import CORS
from timetable import generate_ics
from gyft import get_courses
import base64
from PIL import Image
from timetable.image_parser.table_parser import parse_table
from timetable.image_parser.build_courses_from_image import build_courses_from_image


app = Flask(__name__)
Expand Down Expand Up @@ -201,6 +205,49 @@ def download_ics():
)
except Exception as e:
return jsonify({"status": "error", "message": str(e)}), 500


@app.route("/parse_image", methods=["POST"])
def image_parser():
try:
# print("Hello", request)
data = request.form

all_fields = {
"image": data.get("image"),
}


missing = check_missing_fields(all_fields)
if len(missing) > 0:
return ErpResponse(
False, f"Missing Fields: {', '.join(missing)}", status_code=400
).to_response()
image = all_fields["image"]

if image.startswith("data:image"):
image = image.split(",")[1]

image_data = io.BytesIO(base64.b64decode(image))
data = parse_table(Image.open(image_data))

courses = build_courses_from_image(data)

ics_content = generate_ics(courses, "")

# Create an in-memory file-like object for the ics content
ics_file = io.BytesIO()
ics_file.write(ics_content.encode("utf-8"))
ics_file.seek(0)

return send_file(
ics_file,
as_attachment=True,
mimetype="text/calendar",
download_name="timetable.ics",
)
except Exception as e:
return ErpResponse(False, str(e), status_code=500).to_response()


if __name__ == "__main__":
Expand Down
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@hookform/resolvers": "^3.6.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-dropzone": "^14.3.5",
"react-hook-form": "^7.51.5",
"react-hot-toast": "^2.4.1",
"react-router-dom": "^6.23.1",
Expand Down
55 changes: 55 additions & 0 deletions frontend/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Modal from "./components/Modal";

const App: React.FC = () => {
const [openModal, setOpenModal] = useState(false);
const [modalContent, setModalContent] = useState<React.ReactNode | undefined>();
return (
<>
<main>
Expand All @@ -18,7 +19,7 @@ const App: React.FC = () => {
<div className="wrapper-item">
<div id="wrapped">
<Header />
<MultiForm />
<MultiForm openModal={setOpenModal} setModalContent={setModalContent} />
</div>
<Footer openModal={setOpenModal} />
</div>
Expand All @@ -27,7 +28,7 @@ const App: React.FC = () => {
</aside>
</div>
</main>
{openModal && <Modal closeModal={setOpenModal} />}
{openModal && <Modal closeModal={setOpenModal} modalContent={modalContent} setModalContent={setModalContent} />}
</>
);
};
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/AppContext/AppContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface IUser {
sessionToken: string | null;
}

interface IAuth {
export interface IAuth {
user: IUser;
currentStep: number;
}
Expand Down
51 changes: 30 additions & 21 deletions frontend/src/components/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@ import React from "react";

interface ModalProps {
closeModal: React.Dispatch<React.SetStateAction<boolean>>;
setModalContent?: React.Dispatch<React.SetStateAction<React.ReactNode | undefined>>;
modalContent?: React.ReactNode;
}

const Modal: React.FC<ModalProps> = ({ closeModal }) => {
const Modal: React.FC<ModalProps> = ({ closeModal, setModalContent, modalContent }) => {
return (
<div className="modal-overlay">
<div className="modal-content">
<button
className="close-button"
onClick={() => closeModal(false)}
onClick={() => {
closeModal(false);
if(setModalContent) setModalContent(undefined);
}}
>
<svg
fill="none"
Expand All @@ -27,25 +32,29 @@ const Modal: React.FC<ModalProps> = ({ closeModal }) => {
/>
</svg>
</button>
<h2>GYFT - MetaKGP</h2>
<p>
GYFT gives you an ICS file for your current semester
timetable which you can add in any common calendar
application. Now, you'll always know when your classes
are—whether you decide to go or not!
</p>
<h4>How to get your timetable?</h4>
<ol>
<li>Enter your roll number and password for ERP login</li>
<li>Answer the security question and enter the OTP.</li>
<li>
Download the timetable for the current semester in .ics
format.
</li>
<li>
Import the .ics file into your favorite calendar app!
</li>
</ol>
{modalContent ? modalContent : (
<>
<h2>GYFT - MetaKGP</h2>
<p>
GYFT gives you an ICS file for your current semester
timetable which you can add in any common calendar
application. Now, you'll always know when your classes
are—whether you decide to go or not!
</p>
<h4>How to get your timetable?</h4>
<ol>
<li>Enter your roll number and password for ERP login</li>
<li>Answer the security question and enter the OTP.</li>
<li>
Download the timetable for the current semester in .ics
format.
</li>
<li>
Import the .ics file into your favorite calendar app!
</li>
</ol>
</>
)}
</div>
</div>
);
Expand Down
13 changes: 10 additions & 3 deletions frontend/src/components/MultiForm.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import React from "react";
import React, { useState } from "react";
import RollForm from "./RollForm";
import SecurityQueForm from "./SecurityQueForm";
import { useAppContext } from "../AppContext/AppContext";
import Timetable from "./Timetable";
import ErrorPage from "./ErrorPage";

const MultiForm: React.FC = () => {
type props = {
openModal: React.Dispatch<React.SetStateAction<boolean>>;
setModalContent: React.Dispatch<React.SetStateAction<React.ReactNode | undefined>>;
};

const MultiForm: React.FC<props> = ({ openModal, setModalContent }) => {
const { currentStep, user } = useAppContext();
const [ timeTableFile, setTimeTableFile ] = useState(undefined as Blob | undefined);
if(currentStep == 3 && timeTableFile) return <Timetable file={timeTableFile} />;
if (currentStep == 2 && user.sessionToken && user.ssoToken)
return <Timetable />;
if (currentStep == 1 && user.sessionToken && user.securityQuestion)
return <SecurityQueForm />;
if (currentStep == 0) return <RollForm />;
if (currentStep == 0) return <RollForm openModal={openModal} setModalContent={setModalContent} setTimeTableFile={setTimeTableFile} />;
return <ErrorPage />;
};

Expand Down
Loading