diff --git a/.DS_Store b/.DS_Store index 192bf81..46ae250 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/task-server/app.py b/task-server/app.py index 2064ee2..34c3894 100644 --- a/task-server/app.py +++ b/task-server/app.py @@ -3,7 +3,8 @@ import pandas as pd import matplotlib.pyplot as plt import os import openai - +import joblib +import random app = Flask(__name__) openai.api_key = os.getenv("OPENAI_API_KEY") @@ -12,67 +13,116 @@ openai.api_key = os.getenv("OPENAI_API_KEY") tasks_data = {} users_data = {} -def generate_prompt(task_form): - base_text = "You are a software engineer at a software development company. Your job is to assign tags to tasks based on the software/tools used. Please generate one-word tags representing software/tools/libraries commonly used by developers to build or complete the following task." - return base_text + "TASK: " + task_form +# Loads the SVM model, vectorizer, and encoder +model_filename = "svm_model.sav" +svm_model = joblib.load(model_filename) + +vectorizer_filename = 'fitted_vectorizer.joblib' +word_vectorizer = joblib.load(vectorizer_filename) + +encoder_filename = 'fitted_encoder.joblib' +encoder = joblib.load(encoder_filename) def generate_technical_tags(task_form): - prompt = generate_prompt(task_form) - - response = openai.Completion.create( - model="text-davinci-003", - prompt=prompt, - temperature=0, - max_tokens=100, - stop=["TASK:"] - ) + # Vectorize + vectorized_text = word_vectorizer.transform([task_form]) + decision_function_scores = svm_model.decision_function(vectorized_text) - tags = response.choices[0].text.strip().split("\n") + # Get the top 3 predicted labels based on highest decision function scores + top_4_indices = decision_function_scores.argsort()[0][-3:][::-1] + top_4_labels = encoder.inverse_transform(top_4_indices) + + tags = top_4_labels.tolist() return tags +def match_tasks_to_users(tasks, users, user_max_tasks): + task_ids = list(tasks.keys()) + + # Create a dictionary to store the matched tasks and users + task_to_user_matches = {} + user_to_task_matches = {user_id: None for user_id in users} + + # Helper function to calculate the preference score between a task and a user + def calculate_preference(task_id, user_id): + task_tags = set(tasks[task_id]['tags']) + user_strengths = set(users[user_id]['strengths']) + user_current_tasks = users[user_id]['current_tasks'] + + # Calculate the preference score based on matching tags and strengths + tag_score = len(task_tags.intersection(user_strengths)) + + # Calculate the total preference score, considering currentTasks as a tiebreaker + preference_score = tag_score - user_current_tasks * 0.1 + + # Makes the preference score negative if the user is already at max tasks + if user_current_tasks == user_max_tasks: + preference_score -= user_max_tasks + + return preference_score + + # Assign tasks to users based on preferences + for task_id in task_ids: + task_info = tasks[task_id] + + # Sort the users based on their preference for this task + sorted_users = sorted(users.keys(), key=lambda user_id: calculate_preference(task_id, user_id), reverse=True) + # Assign the task to the first user in the sorted list who is not already matched + for user_id in sorted_users: + if user_to_task_matches[user_id] is None: + task_to_user_matches[task_id] = user_id + user_to_task_matches[user_id] = task_id + break + print(task_to_user_matches) + return task_to_user_matches + @app.route('/label_tasks', methods=['POST']) def get_task_tags(): - # try: - data = request.get_json() - tasks = data[0]['tasks'] - result = {} - for task, task_description in tasks.items(): - task_id = task_description["id"] - task_content = task_description["content"] - task_complexity = task_description["complexityScore"] - - tags = generate_technical_tags(task_content) # where the function that gets the tags is placed - result[task_id] = tags - return jsonify(result) - # except Exception as e: - # return jsonify({"error": str(e)}), 500 - - -@app.route('/assign_tasks', methods=['POST']) -def assign_tasks_to_users(): try: data = request.get_json() tasks = data[0]['tasks'] - users = data[0]['users'] result = {} - - tasks_dict = {} - users_dict = {} - for task, task_description in tasks.items(): - tasks_dict[task_description["id"]] = {"tags" : task_description["content"], "complexity" : task_description["complexityScore"]} - # tasks_dict[task_description["id"]] = {"tags" : generate_technical_tags(task_description["content"]), "complexity" : task_description["complexityScore"]} - - for user, user_description in users.items(): - users_dict[user_description["id"]] = {"strengths" : user_description["strengths"], "current_tasks" : user_description["currentTasks"]} - - - - # return jsonify(result) - return result + task_id = task_description["id"] + task_content = task_description["content"] + task_complexity = task_description["complexityScore"] + + tags = generate_technical_tags(task_content) # where the function that gets the tags is placed + result[task_id] = tags + return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 +@app.route('/assign_tasks', methods=['POST']) +def assign_tasks_to_users(): + # try: + data = request.get_json() + tasks = data[0]['tasks'] + users = data[0]['users'] + result = {} + + tasks_dict = {} + users_dict = {} + + for task, task_description in tasks.items(): + tasks_dict[task_description["id"]] = {"tags" : generate_technical_tags(task_description["content"]), "complexity" : task_description["complexityScore"]} + + for user, user_description in users.items(): + users_dict[user_description["id"]] = {"strengths" : user_description["strengths"], "current_tasks" : user_description["currentTasks"]} + + + matching = match_tasks_to_users(tasks_dict, users_dict, 3) + # result = {} + # for task, task_description in tasks.items(): + # result[task_description["content"]] = {"assignedTo" : matchings[task_description["id"]]} + + # print(result) + result = matching + return jsonify(result) + # return result + # except Exception as e: + # return jsonify({"error": str(e)}), 500 + + if __name__ == '__main__': app.run(debug=True) diff --git a/task-server/fitted_encoder.joblib b/task-server/fitted_encoder.joblib new file mode 100644 index 0000000..5c9f29f Binary files /dev/null and b/task-server/fitted_encoder.joblib differ diff --git a/task-server/fitted_vectorizer.joblib b/task-server/fitted_vectorizer.joblib new file mode 100644 index 0000000..dd0615a Binary files /dev/null and b/task-server/fitted_vectorizer.joblib differ diff --git a/task-server/model_integration_testing.py b/task-server/model_integration_testing.py new file mode 100644 index 0000000..aec0589 --- /dev/null +++ b/task-server/model_integration_testing.py @@ -0,0 +1,28 @@ +import joblib + +# To be translated +sample_text = "Implement a login page with form validation using React.js." + +# Loads the model, vectorizer, and encoder +model_filename = "svm_model.sav" +svm_model = joblib.load(model_filename) + +vectorizer_filename = 'fitted_vectorizer.joblib' +word_vectorizer = joblib.load(vectorizer_filename) + +encoder_filename = 'fitted_encoder.joblib' +encoder = joblib.load(encoder_filename) + +# Vectorize +vectorized_text = word_vectorizer.transform([sample_text]) + +binary_predictions = svm_model.predict(vectorized_text) + +decision_function_scores = svm_model.decision_function(vectorized_text) + +# Get the top 3 predicted labels based on highest decision function scores +top_4_indices = decision_function_scores.argsort()[0][-3:][::-1] + +top_4_labels = encoder.inverse_transform(top_4_indices) + +print("Top 3 predicted labels:", top_4_labels) diff --git a/task-server/svm_model.sav b/task-server/svm_model.sav new file mode 100644 index 0000000..0e003fe Binary files /dev/null and b/task-server/svm_model.sav differ diff --git a/task-server/users_tasks.json b/task-server/users_tasks.json index d1f4b5e..1acec08 100644 --- a/task-server/users_tasks.json +++ b/task-server/users_tasks.json @@ -79,81 +79,82 @@ }, "users" : { - "1" : { + "1": { "id": "1", "currentTasks": 0, - "strengths" : [] + "strengths": ["Objective-C", "Java", "Elasticsearch"] }, - "2" : { + "2": { "id": "2", "currentTasks": 0, - "strengths" : [] + "strengths": ["SQL", "TypeScript", "jQuery"] }, - "3" : { + "3": { "id": "3", "currentTasks": 0, - "strengths" : [] + "strengths": ["Java", "Bootstrap", "jQuery"] }, - "4" : { + "4": { "id": "4", "currentTasks": 0, - "strengths" : [] + "strengths": ["HTML/CSS", "Adobe Creative Suite", "C++"] }, - "5" : { + "5": { "id": "5", "currentTasks": 0, - "strengths" : [] + "strengths": ["SQL", "PostgreSQL", "Java"] }, - "6" : { + "6": { "id": "6", "currentTasks": 0, - "strengths" : [] + "strengths": ["ReactJS", "JavaScript", "Bootstrap"] }, - "7" : { + "7": { "id": "7", "currentTasks": 0, - "strengths" : [] + "strengths": ["Ruby", "ReactJS", "Firebase"] }, - "8" : { + "8": { "id": "8", "currentTasks": 0, - "strengths" : [] + "strengths": ["Node.js", "JavaScript", "RESTful APIs"] }, - "9" : { + "9": { "id": "9", "currentTasks": 0, - "strengths" : [] + "strengths": ["HTML/CSS", "jQuery", "Go"] }, - "10" : { + "10": { "id": "10", "currentTasks": 0, - "strengths" : [] + "strengths": ["JavaScript", "ReactJS", "C++"] }, - "11" : { + "11": { "id": "11", "currentTasks": 0, - "strengths" : [] + "strengths": [".NET", "API Integration", "PHP"] }, - "12" : { + "12": { "id": "12", "currentTasks": 0, - "strengths" : [] + "strengths": ["SQL", "MongoDB", "Azure"] }, - "13" : { + "13": { "id": "13", "currentTasks": 0, - "strengths" : [] + "strengths": ["API Integration", "RESTful APIs", "Linux/Unix"] }, - "14" : { + "14": { "id": "14", "currentTasks": 0, - "strengths" : [] + "strengths": ["Ruby", "ReactJS", "C++"] }, - "15" : { + "15": { "id": "15", "currentTasks": 0, - "strengths" : [] + "strengths": ["JavaScript", "ReactJS", "Bootstrap"] } } + } ] \ No newline at end of file