Spaces:
Sleeping
Sleeping
Upload 7 files
Browse files- Procfile +1 -0
- app.py +133 -0
- requirements.txt +6 -0
- templates/addcourse.html +29 -0
- templates/addprogram.html +18 -0
- templates/index.html +289 -0
- templates/result.html +46 -0
Procfile
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
web: gunicorn app:app
|
app.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
ο»Ώfrom flask import Flask, request, render_template, redirect, url_for
|
| 2 |
+
from transformers import AutoTokenizer, AutoModel
|
| 3 |
+
import torch
|
| 4 |
+
import os
|
| 5 |
+
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
| 6 |
+
app = Flask(__name__)
|
| 7 |
+
|
| 8 |
+
# Dictionary to store programs and their courses
|
| 9 |
+
programs = {}
|
| 10 |
+
|
| 11 |
+
# Default model name
|
| 12 |
+
current_model_name = 'sentence-transformers/all-mpnet-base-v2'
|
| 13 |
+
|
| 14 |
+
# Function to load the tokenizer and model dynamically
|
| 15 |
+
def load_model_and_tokenizer(model_name):
|
| 16 |
+
try:
|
| 17 |
+
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
| 18 |
+
model = AutoModel.from_pretrained(model_name)
|
| 19 |
+
return tokenizer, model, None
|
| 20 |
+
except Exception as e:
|
| 21 |
+
return None, None, str(e)
|
| 22 |
+
|
| 23 |
+
# Load the initial model and tokenizer
|
| 24 |
+
tokenizer, model, error = load_model_and_tokenizer(current_model_name)
|
| 25 |
+
|
| 26 |
+
def mean_pooling(token_embeddings, mask):
|
| 27 |
+
"""Applies mean pooling to token embeddings, considering the mask."""
|
| 28 |
+
mask = mask.unsqueeze(-1).expand(token_embeddings.size())
|
| 29 |
+
sum_embeddings = torch.sum(token_embeddings * mask, dim=1)
|
| 30 |
+
sum_mask = torch.clamp(mask.sum(dim=1), min=1e-9) # Avoid division by zero
|
| 31 |
+
return sum_embeddings / sum_mask
|
| 32 |
+
|
| 33 |
+
def compute_plo_embeddings():
|
| 34 |
+
"""Computes embeddings for the predefined PLOs."""
|
| 35 |
+
tokens = tokenizer(plos, padding=True, truncation=True, return_tensors='pt')
|
| 36 |
+
mask = tokens['attention_mask']
|
| 37 |
+
with torch.no_grad():
|
| 38 |
+
outputs = model(**tokens)
|
| 39 |
+
return mean_pooling(outputs.last_hidden_state, mask)
|
| 40 |
+
|
| 41 |
+
# Predefined Program Learning Outcomes (PLOs)
|
| 42 |
+
plos = [
|
| 43 |
+
"Analyze a complex computing problem and apply principles of computing and other relevant disciplines to identify solutions.",
|
| 44 |
+
"Design, implement, and evaluate a computing-based solution to meet a given set of computing requirements.",
|
| 45 |
+
"Communicate effectively in a variety of professional contexts.",
|
| 46 |
+
"Recognize professional responsibilities and make informed judgments in computing practice based on legal and ethical principles.",
|
| 47 |
+
"Function effectively as a member or leader of a team engaged in activities appropriate to the programβs discipline.",
|
| 48 |
+
"Support the delivery, use, and management of information systems within an information systems environment."
|
| 49 |
+
]
|
| 50 |
+
|
| 51 |
+
# Compute PLO embeddings (once at startup)
|
| 52 |
+
plo_embeddings = compute_plo_embeddings()
|
| 53 |
+
|
| 54 |
+
def get_similarity(input_sentence):
|
| 55 |
+
"""Calculates the similarity between an input sentence and predefined PLOs."""
|
| 56 |
+
tokens = tokenizer(input_sentence, padding=True, truncation=True, return_tensors='pt')
|
| 57 |
+
mask = tokens['attention_mask']
|
| 58 |
+
|
| 59 |
+
with torch.no_grad():
|
| 60 |
+
outputs = model(**tokens)
|
| 61 |
+
input_embedding = mean_pooling(outputs.last_hidden_state, mask)
|
| 62 |
+
|
| 63 |
+
similarities = torch.nn.functional.cosine_similarity(input_embedding, plo_embeddings)
|
| 64 |
+
return similarities
|
| 65 |
+
|
| 66 |
+
@app.route('/')
|
| 67 |
+
def index():
|
| 68 |
+
"""Home page displaying current programs and model status."""
|
| 69 |
+
return render_template('index.html', programs=programs, model_name=current_model_name)
|
| 70 |
+
|
| 71 |
+
@app.route('/set_model', methods=['POST'])
|
| 72 |
+
def set_model():
|
| 73 |
+
"""Allows users to dynamically change the model."""
|
| 74 |
+
global tokenizer, model, plo_embeddings, current_model_name
|
| 75 |
+
|
| 76 |
+
model_name = request.form['model_name']
|
| 77 |
+
tokenizer, model, error = load_model_and_tokenizer(model_name)
|
| 78 |
+
|
| 79 |
+
if error:
|
| 80 |
+
return render_template('index.html', programs=programs, message=f"Error loading model: {error}")
|
| 81 |
+
|
| 82 |
+
# Update the global model name and recompute embeddings
|
| 83 |
+
current_model_name = model_name
|
| 84 |
+
plo_embeddings = compute_plo_embeddings()
|
| 85 |
+
return redirect(url_for('index'))
|
| 86 |
+
|
| 87 |
+
@app.route('/addprogram', methods=['GET', 'POST'])
|
| 88 |
+
def add_program():
|
| 89 |
+
"""Adds a new program."""
|
| 90 |
+
if request.method == 'POST':
|
| 91 |
+
program_name = request.form['program_name']
|
| 92 |
+
if program_name not in programs:
|
| 93 |
+
programs[program_name] = {} # Initialize an empty dictionary for courses
|
| 94 |
+
return redirect(url_for('index'))
|
| 95 |
+
return render_template('addprogram.html')
|
| 96 |
+
|
| 97 |
+
@app.route('/addcourse', methods=['GET', 'POST'])
|
| 98 |
+
def create_course():
|
| 99 |
+
"""Creates a new course under a specific program."""
|
| 100 |
+
if request.method == 'POST':
|
| 101 |
+
program_name = request.form['program']
|
| 102 |
+
course_name = request.form['course_name']
|
| 103 |
+
outcomes = request.form['course_outcomes'].split('\n')
|
| 104 |
+
|
| 105 |
+
if program_name in programs:
|
| 106 |
+
programs[program_name][course_name] = outcomes # Add course to the selected program
|
| 107 |
+
|
| 108 |
+
return redirect(url_for('index'))
|
| 109 |
+
return render_template('addcourse.html', programs=programs)
|
| 110 |
+
|
| 111 |
+
@app.route('/match', methods=['POST'])
|
| 112 |
+
def match_outcomes():
|
| 113 |
+
"""Matches course outcomes with predefined PLOs."""
|
| 114 |
+
course_name = request.form['course']
|
| 115 |
+
print(course_name)
|
| 116 |
+
course_outcomes = request.form['course_outcomes'].split('\n')
|
| 117 |
+
results = []
|
| 118 |
+
|
| 119 |
+
for co in course_outcomes:
|
| 120 |
+
co = co.strip()
|
| 121 |
+
if co: # Ensure the outcome is not empty
|
| 122 |
+
similarities = get_similarity(co)
|
| 123 |
+
top_matches_indices = similarities.topk(3).indices.tolist()
|
| 124 |
+
results.append({
|
| 125 |
+
'course_outcome': co,
|
| 126 |
+
'course_name' : course_name,
|
| 127 |
+
'best_matches': top_matches_indices
|
| 128 |
+
})
|
| 129 |
+
|
| 130 |
+
return render_template('result.html', course_name =course_name, results=results)
|
| 131 |
+
|
| 132 |
+
if __name__ == '__main__':
|
| 133 |
+
app.run(debug=True)
|
requirements.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Flask
|
| 2 |
+
transformers
|
| 3 |
+
torch
|
| 4 |
+
Gunicorn
|
| 5 |
+
pandas
|
| 6 |
+
openpyxl
|
templates/addcourse.html
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<title>Add Course</title>
|
| 6 |
+
</head>
|
| 7 |
+
<body>
|
| 8 |
+
<h1>Add a New Course</h1>
|
| 9 |
+
<form action="/addcourse" method="post">
|
| 10 |
+
<label for="program">Select Program:</label><br>
|
| 11 |
+
<select id="program" name="program" required>
|
| 12 |
+
<option value="">--Select a program--</option>
|
| 13 |
+
{% for program in programs %}
|
| 14 |
+
<option value="{{ program }}">{{ program }}</option>
|
| 15 |
+
{% endfor %}
|
| 16 |
+
</select><br><br>
|
| 17 |
+
|
| 18 |
+
<label for="course_name">Course Name:</label><br>
|
| 19 |
+
<input type="text" id="course_name" name="course_name" required><br><br>
|
| 20 |
+
|
| 21 |
+
<label for="course_outcomes">Course Learning Outcomes (one per line):</label><br>
|
| 22 |
+
<textarea id="course_outcomes" name="course_outcomes" rows="5" cols="50" required></textarea><br><br>
|
| 23 |
+
|
| 24 |
+
<input type="submit" value="Add Course">
|
| 25 |
+
</form>
|
| 26 |
+
<br>
|
| 27 |
+
<a href="/">Back to Home</a>
|
| 28 |
+
</body>
|
| 29 |
+
</html>
|
templates/addprogram.html
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Add Program</title>
|
| 7 |
+
</head>
|
| 8 |
+
<body>
|
| 9 |
+
<h1>Add a New Program</h1>
|
| 10 |
+
<form action="/addprogram" method="post">
|
| 11 |
+
<label for="program_name">Program Name:</label><br>
|
| 12 |
+
<input type="text" id="program_name" name="program_name" required><br><br>
|
| 13 |
+
<input type="submit" value="Add Program">
|
| 14 |
+
</form>
|
| 15 |
+
<br>
|
| 16 |
+
<a href="/">Back to Home</a>
|
| 17 |
+
</body>
|
| 18 |
+
</html>
|
templates/index.html
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
| 7 |
+
<title>Fibrosis</title>
|
| 8 |
+
<link rel="shortcut icon"
|
| 9 |
+
href="" />
|
| 10 |
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
|
| 11 |
+
<style>
|
| 12 |
+
body{background-color: #eff2f9;}
|
| 13 |
+
.iupload h3{color: #1b2d6b;font-size: 30px;font-weight: 700;}
|
| 14 |
+
.img-part{height:300px;width:300px;margin:0px auto;}
|
| 15 |
+
.image-part{height:300px;width:300px;border:1px solid #1b2d6b;}
|
| 16 |
+
.image-part img{position:absolute;height: 300px;width:300px;display:none;padding:5px;}
|
| 17 |
+
.image-part #video{display:block;height: 300px;width:300px;padding:5px;}
|
| 18 |
+
.res-part{border:1px solid #dedede;margin-left:20px;height: 310px;width:100%;padding:5px;margin:0px auto;overflow:auto;}
|
| 19 |
+
.res-part2{border:1px solid #dedede;height: 310px;width:100%;padding:5px;margin:0px auto;}
|
| 20 |
+
.resp-img{height: 298px;width: 233px;margin:0px auto;}
|
| 21 |
+
.jsonRes{margin-left:30px;}
|
| 22 |
+
#send{cursor:pointer;}
|
| 23 |
+
.btn-part{width:325px;}
|
| 24 |
+
textarea,
|
| 25 |
+
select,
|
| 26 |
+
.form-control,
|
| 27 |
+
.custom-select,
|
| 28 |
+
button.btn,
|
| 29 |
+
.btn-primary,
|
| 30 |
+
input[type="text"],
|
| 31 |
+
input[type="url"],
|
| 32 |
+
.uneditable-input{
|
| 33 |
+
border: 1px solid #363e75;
|
| 34 |
+
outline: 0 !important;
|
| 35 |
+
border-radius:0px;
|
| 36 |
+
box-shadow: none;
|
| 37 |
+
-webkit-box-shadow: none;
|
| 38 |
+
-moz-box-shadow: none;
|
| 39 |
+
-moz-transition: none;
|
| 40 |
+
-webkit-transition: none;
|
| 41 |
+
}
|
| 42 |
+
textarea:focus,
|
| 43 |
+
select:focus,
|
| 44 |
+
.form-control:focus,
|
| 45 |
+
.btn:focus,
|
| 46 |
+
.btn-primary:focus,
|
| 47 |
+
.custom-select:focus,
|
| 48 |
+
input[type="text"]:focus,
|
| 49 |
+
.uneditable-input:focus{
|
| 50 |
+
border: 1px solid #007bff;
|
| 51 |
+
outline: 0 !important;
|
| 52 |
+
border-radius:0px;
|
| 53 |
+
box-shadow: none;
|
| 54 |
+
-webkit-box-shadow: none;
|
| 55 |
+
-moz-box-shadow: none;
|
| 56 |
+
-moz-transition: none;
|
| 57 |
+
-webkit-transition: none;
|
| 58 |
+
}
|
| 59 |
+
#loading {
|
| 60 |
+
position: fixed;
|
| 61 |
+
left: 0px;
|
| 62 |
+
top: 0px;
|
| 63 |
+
width: 100%;
|
| 64 |
+
height: 100%;
|
| 65 |
+
z-index: 9999999999;
|
| 66 |
+
overflow: hidden;
|
| 67 |
+
background: rgba(255, 255, 255, 0.7);
|
| 68 |
+
}
|
| 69 |
+
.loader {
|
| 70 |
+
border: 8px solid #f3f3f3;
|
| 71 |
+
border-top: 8px solid #363e75;
|
| 72 |
+
border-radius: 50%;
|
| 73 |
+
width: 60px;
|
| 74 |
+
height: 60px;
|
| 75 |
+
left: 50%;
|
| 76 |
+
margin-left: -4em;
|
| 77 |
+
display: block;
|
| 78 |
+
animation: spin 2s linear infinite;
|
| 79 |
+
}
|
| 80 |
+
.loader,
|
| 81 |
+
.loader:after {display: block;position: absolute;top: 50%;margin-top: -4.05em;}
|
| 82 |
+
@keyframes spin {
|
| 83 |
+
0% {
|
| 84 |
+
transform: rotate(0deg);
|
| 85 |
+
}
|
| 86 |
+
100% {
|
| 87 |
+
transform: rotate(360deg);
|
| 88 |
+
}
|
| 89 |
+
}
|
| 90 |
+
.right-part{border:1px solid #dedede;padding:5px;}
|
| 91 |
+
.logo{position:absolute;right:0px;bottom:0px;margin-right:30px;margin-bottom:30px;}
|
| 92 |
+
</style>
|
| 93 |
+
<script>
|
| 94 |
+
let programs = {{ programs|tojson }}; // Passing programs from the backend as a JSON object
|
| 95 |
+
|
| 96 |
+
// Update the courses dropdown when a program is selected
|
| 97 |
+
function updateCourses() {
|
| 98 |
+
const programSelect = document.getElementById("program");
|
| 99 |
+
const courseSelect = document.getElementById("course");
|
| 100 |
+
const selectedProgram = programSelect.value;
|
| 101 |
+
|
| 102 |
+
// Clear existing options in the course dropdown
|
| 103 |
+
courseSelect.innerHTML = "<option value=''>--Select a course--</option>";
|
| 104 |
+
document.getElementById("course_outcomes").value = ""; // Clear the course outcomes
|
| 105 |
+
|
| 106 |
+
if (selectedProgram && programs[selectedProgram]) {
|
| 107 |
+
const courses = Object.keys(programs[selectedProgram]);
|
| 108 |
+
courses.forEach(course => {
|
| 109 |
+
let option = document.createElement("option");
|
| 110 |
+
option.value = course;
|
| 111 |
+
option.text = course;
|
| 112 |
+
courseSelect.appendChild(option);
|
| 113 |
+
});
|
| 114 |
+
}
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
// Update the course outcomes in the listbox when a course is selected
|
| 118 |
+
function updateCourseOutcomes() {
|
| 119 |
+
const programSelect = document.getElementById("program").value;
|
| 120 |
+
const courseSelect = document.getElementById("course").value;
|
| 121 |
+
const courseOutcomes = programs[programSelect] ? programs[programSelect][courseSelect] : [];
|
| 122 |
+
|
| 123 |
+
const courseOutcomesTextArea = document.getElementById("course_outcomes");
|
| 124 |
+
courseOutcomesTextArea.value = courseOutcomes ? courseOutcomes.join("\n") : "";
|
| 125 |
+
}
|
| 126 |
+
</script>
|
| 127 |
+
</head>
|
| 128 |
+
<body>
|
| 129 |
+
<div class="main container">
|
| 130 |
+
<section class="iupload">
|
| 131 |
+
<h3 class="text-center py-4">CLOs - PLOs Mapping Using Large Language Models </h3>
|
| 132 |
+
<div class="row">
|
| 133 |
+
<div class="img-part col-md-6">
|
| 134 |
+
<h1> </h1>
|
| 135 |
+
|
| 136 |
+
<!-- Section for Model Selection -->
|
| 137 |
+
<h5>Select Model</h5>
|
| 138 |
+
<form action="/set_model" method="post">
|
| 139 |
+
<label for="model_name">Choose a Model:</label><br>
|
| 140 |
+
<select id="model_name" name="model_name">
|
| 141 |
+
<option value="sentence-transformers/all-mpnet-base-v2" selected>all-mpnet-base-v2</option>
|
| 142 |
+
<option value="sentence-transformers/bert-base-nli-mean-tokens">bert-base-nli-mean-tokens</option>
|
| 143 |
+
<option value="thuan9889/llama_embedding_model_v1">llama_embedding_model_v1</option>
|
| 144 |
+
<option value="sembeddings/model_gpt_trained">model_gpt_trained</option>
|
| 145 |
+
</select>
|
| 146 |
+
<button type="submit">Set Model</button>
|
| 147 |
+
</form>
|
| 148 |
+
|
| 149 |
+
<h5>Current Model: {{ model_name }}</h5>
|
| 150 |
+
{% if message %}
|
| 151 |
+
<p style="color: red;">{{ message }}</p>
|
| 152 |
+
{% endif %}
|
| 153 |
+
|
| 154 |
+
<hr>
|
| 155 |
+
|
| 156 |
+
<!-- Section for Course Outcome Matching -->
|
| 157 |
+
<h5>Select Program and Course</h5>
|
| 158 |
+
<form action="/match" method="post">
|
| 159 |
+
<label for="program">Select Program:</label><br>
|
| 160 |
+
<select id="program" name="program" onChange="updateCourses()">
|
| 161 |
+
<option value="">--Select a program--</option>
|
| 162 |
+
{% for program in programs %}
|
| 163 |
+
<option value="{{ program }}">{{ program }}</option>
|
| 164 |
+
{% endfor %}
|
| 165 |
+
</select><br><br>
|
| 166 |
+
|
| 167 |
+
<label for="course">Select Course:</label><br>
|
| 168 |
+
<select id="course" name="course" onChange="updateCourseOutcomes()">
|
| 169 |
+
<option value="">--Select a course--</option>
|
| 170 |
+
</select><br><br>
|
| 171 |
+
|
| 172 |
+
<label for="course_outcomes">Course Outcomes:</label><br>
|
| 173 |
+
<textarea id="course_outcomes" name="course_outcomes" rows="10" cols="50" readonly></textarea><br><br>
|
| 174 |
+
|
| 175 |
+
<input type="submit" value="Submit">
|
| 176 |
+
</form>
|
| 177 |
+
</div>
|
| 178 |
+
<div class="col-md-6 col-xs-12 right-part">
|
| 179 |
+
<h5 class="mb-2"><center>Create a Program or Course</center></h5>
|
| 180 |
+
<div class="row">
|
| 181 |
+
|
| 182 |
+
<div class="res-part col-md-5 col-xs-12"><div class="jsonRes"><h2></h2>
|
| 183 |
+
<ul>
|
| 184 |
+
<li><a href="/addprogram">Create Program</a></li>
|
| 185 |
+
<li><a href="/addcourse">Create Course</a></li>
|
| 186 |
+
</ul></div></div>
|
| 187 |
+
</div>
|
| 188 |
+
</div>
|
| 189 |
+
</div>
|
| 190 |
+
</section>
|
| 191 |
+
</div>
|
| 192 |
+
|
| 193 |
+
<img class="logo" src="" />
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
<div id="loading"></div>
|
| 197 |
+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
|
| 198 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
|
| 199 |
+
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
|
| 200 |
+
|
| 201 |
+
<script>
|
| 202 |
+
var mybtn = document.getElementById('startbtn');
|
| 203 |
+
var myvideo = document.getElementById('video');
|
| 204 |
+
var mycanvas = document.getElementById('canvas');
|
| 205 |
+
var myphoto = document.getElementById('photo');
|
| 206 |
+
var base_data = "";
|
| 207 |
+
|
| 208 |
+
function sendRequest(base64Data){
|
| 209 |
+
var type = "json";
|
| 210 |
+
if(base64Data != "" || base64Data != null){
|
| 211 |
+
if(type == "imgtobase"){
|
| 212 |
+
$(".res-part").html("");
|
| 213 |
+
$(".res-part").html(base64Data);
|
| 214 |
+
}
|
| 215 |
+
else if(type == "basetoimg"){
|
| 216 |
+
var imageData = $("#imgstring").val();
|
| 217 |
+
$(".res-part").html("");
|
| 218 |
+
$(".res-part").append("<img src='data:image/jpeg;base64," + imageData + "' alt='' />");
|
| 219 |
+
}
|
| 220 |
+
else{
|
| 221 |
+
var url = $("#url").val();
|
| 222 |
+
$("#loading").show();
|
| 223 |
+
$.ajax({
|
| 224 |
+
url : url,
|
| 225 |
+
type: "post",
|
| 226 |
+
cache: false,
|
| 227 |
+
async: true,
|
| 228 |
+
crossDomain: true,
|
| 229 |
+
headers: {
|
| 230 |
+
'Content-Type': 'application/json',
|
| 231 |
+
'Access-Control-Allow-Origin':'*'
|
| 232 |
+
},
|
| 233 |
+
data:JSON.stringify({image:base64Data}),
|
| 234 |
+
success: function(res){
|
| 235 |
+
$(".res-part").html("");
|
| 236 |
+
$(".res-part2").html("");
|
| 237 |
+
try{
|
| 238 |
+
var imageData = res[1].image;
|
| 239 |
+
if(imageData.length > 100){
|
| 240 |
+
if(imageData.length > 10){$(".res-part2").append("<img class='resp-img' src='data:image/jpeg;base64," + imageData + "' alt='' />");}
|
| 241 |
+
}
|
| 242 |
+
}catch(e){}
|
| 243 |
+
$(".res-part").html("<pre>" + JSON.stringify(res[0], undefined, 2) + "</pre>");
|
| 244 |
+
$("#loading").hide();
|
| 245 |
+
}
|
| 246 |
+
});
|
| 247 |
+
}
|
| 248 |
+
}
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
$(document).ready(function(){
|
| 252 |
+
$("#loading").hide();
|
| 253 |
+
|
| 254 |
+
$('#send').click(function(evt){
|
| 255 |
+
sendRequest(base_data);
|
| 256 |
+
});
|
| 257 |
+
|
| 258 |
+
$('#uload').click(function(evt) {
|
| 259 |
+
$('#fileinput').focus().trigger('click');
|
| 260 |
+
});
|
| 261 |
+
$("#fileinput").change(function(){
|
| 262 |
+
if (this.files && this.files[0]){
|
| 263 |
+
var reader = new FileReader();
|
| 264 |
+
reader.onload = function (e){
|
| 265 |
+
var url = e.target.result;
|
| 266 |
+
var img = new Image();
|
| 267 |
+
img.crossOrigin = 'Anonymous';
|
| 268 |
+
img.onload = function(){
|
| 269 |
+
var canvas = document.createElement('CANVAS');
|
| 270 |
+
var ctx = canvas.getContext('2d');
|
| 271 |
+
canvas.height = this.height;
|
| 272 |
+
canvas.width = this.width;
|
| 273 |
+
ctx.drawImage(this, 0, 0);
|
| 274 |
+
base_data = canvas.toDataURL('image/jpeg', 1.0).replace(/^data:image.+;base64,/, '');
|
| 275 |
+
canvas = null;
|
| 276 |
+
};
|
| 277 |
+
img.src = url;
|
| 278 |
+
$('#photo').attr('src', url);
|
| 279 |
+
$('#photo').show();
|
| 280 |
+
$('#video').hide();
|
| 281 |
+
}
|
| 282 |
+
reader.readAsDataURL(this.files[0]);
|
| 283 |
+
}
|
| 284 |
+
});
|
| 285 |
+
});
|
| 286 |
+
|
| 287 |
+
</script>
|
| 288 |
+
</body>
|
| 289 |
+
</html>
|
templates/result.html
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Predicted Mapping</title>
|
| 7 |
+
<style>
|
| 8 |
+
table, th, td {
|
| 9 |
+
border: 1px solid black;
|
| 10 |
+
border-collapse: collapse;
|
| 11 |
+
padding: 10px;
|
| 12 |
+
text-align: left;
|
| 13 |
+
}
|
| 14 |
+
.top-1 { background-color: red; }
|
| 15 |
+
.top-2 { background-color: blue; }
|
| 16 |
+
.top-3 { background-color: green; }
|
| 17 |
+
</style>
|
| 18 |
+
</head>
|
| 19 |
+
<body>
|
| 20 |
+
<h1>Predicted Mapping</h1>
|
| 21 |
+
<h2>Course: {{ course_name }}</h2>
|
| 22 |
+
<table>
|
| 23 |
+
<tr>
|
| 24 |
+
<th>Course Learning Outcome</th>
|
| 25 |
+
<th>PLO1</th>
|
| 26 |
+
<th>PLO2</th>
|
| 27 |
+
<th>PLO3</th>
|
| 28 |
+
<th>PLO4</th>
|
| 29 |
+
<th>PLO5</th>
|
| 30 |
+
<th>PLO6</th>
|
| 31 |
+
</tr>
|
| 32 |
+
{% for result in results %}
|
| 33 |
+
<tr>
|
| 34 |
+
<td>{{ result.course_outcome }}</td>
|
| 35 |
+
<td class="{% if 0 in result.best_matches[:1] %}top-1{% elif 0 in result.best_matches[1:2] %}top-2{% elif 0 in result.best_matches[2:3] %}top-3{% endif %}">{{ 'β' if 0 in result.best_matches else '' }}</td>
|
| 36 |
+
<td class="{% if 1 in result.best_matches[:1] %}top-1{% elif 1 in result.best_matches[1:2] %}top-2{% elif 1 in result.best_matches[2:3] %}top-3{% endif %}">{{ 'β' if 1 in result.best_matches else '' }}</td>
|
| 37 |
+
<td class="{% if 2 in result.best_matches[:1] %}top-1{% elif 2 in result.best_matches[1:2] %}top-2{% elif 2 in result.best_matches[2:3] %}top-3{% endif %}">{{ 'β' if 2 in result.best_matches else '' }}</td>
|
| 38 |
+
<td class="{% if 3 in result.best_matches[:1] %}top-1{% elif 3 in result.best_matches[1:2] %}top-2{% elif 3 in result.best_matches[2:3] %}top-3{% endif %}">{{ 'β' if 3 in result.best_matches else '' }}</td>
|
| 39 |
+
<td class="{% if 4 in result.best_matches[:1] %}top-1{% elif 4 in result.best_matches[1:2] %}top-2{% elif 4 in result.best_matches[2:3] %}top-3{% endif %}">{{ 'β' if 4 in result.best_matches else '' }}</td>
|
| 40 |
+
<td class="{% if 5 in result.best_matches[:1] %}top-1{% elif 5 in result.best_matches[1:2] %}top-2{% elif 5 in result.best_matches[2:3] %}top-3{% endif %}">{{ 'β' if 5 in result.best_matches else '' }}</td>
|
| 41 |
+
</tr>
|
| 42 |
+
{% endfor %}
|
| 43 |
+
</table>
|
| 44 |
+
<a href="/">Back to Input</a>
|
| 45 |
+
</body>
|
| 46 |
+
</html>
|