Spaces:
Runtime error
Runtime error
Commit
·
bb19264
1
Parent(s):
1fa064d
Upload 10 files
Browse files- BreakdownFeature.py +42 -0
- CSIT321Project-Info.txt +123 -0
- GenerationFeature.py +49 -0
- README.md +9 -6
- Refineverse.db +0 -0
- Refineverse.py +225 -0
- TextSummarizationFeature.py +50 -0
- TranslationFeature.py +91 -0
- createDatabase.py +45 -0
- requirements.txt +0 -0
BreakdownFeature.py
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import sqlite3
|
2 |
+
from flask import g
|
3 |
+
from transformers import pipeline
|
4 |
+
|
5 |
+
# Set up zero-shot classification pipeline
|
6 |
+
classifier = pipeline("zero-shot-classification", model="facebook/bart-large-mnli")
|
7 |
+
|
8 |
+
# The main "breakdown" functionality. Performs NLP using zero-shot classification!
|
9 |
+
def breakdown(userStory):
|
10 |
+
|
11 |
+
# The results are stored into a dictionary variable with predefined labels
|
12 |
+
processedStory = classifier(userStory, candidate_labels=["developer", "tester", "project manager", "system admin"])
|
13 |
+
|
14 |
+
# Extract labels and scores
|
15 |
+
scores = processedStory['scores']
|
16 |
+
labels = processedStory['labels']
|
17 |
+
|
18 |
+
# As the index of a score is always equal to its associated label,
|
19 |
+
# We only need to index of the score to find correct label.
|
20 |
+
maxScoreIndex = scores.index(max(scores)) # Gets the index of the highest score/accuracy
|
21 |
+
maxLabel = labels[maxScoreIndex] # Gets the associated label name
|
22 |
+
|
23 |
+
# Return the highest label
|
24 |
+
return maxLabel
|
25 |
+
|
26 |
+
# Function to grab all contents in the "Breakdown" table (except for unique ids)
|
27 |
+
def getBreakdownContents():
|
28 |
+
db = getattr(g, '_database', None) # Gets the _database attribute from the 'g' object. If it does not exist, returns 'None'
|
29 |
+
if db is None:
|
30 |
+
db = g._database = sqlite3.connect('Refineverse.db') # If db is None, create a new connection for db and g._database.
|
31 |
+
cursor = db.cursor() # Creates a cursor object to handle data
|
32 |
+
cursor.execute("SELECT user_story, assignedLabel FROM Breakdown") # The cursor executes the query
|
33 |
+
rows = cursor.fetchall() # Stores the results of fetchall() into a variable
|
34 |
+
return rows
|
35 |
+
|
36 |
+
# Function to insert a new row into the "Breakdown" table
|
37 |
+
def insertBreakdownRow(user_story, assigned_label):
|
38 |
+
with sqlite3.connect('Refineverse.db') as conn: # 'With' will automatically take care of closing and opening the connection
|
39 |
+
cursor = conn.cursor()
|
40 |
+
cursor.execute("INSERT INTO Breakdown (user_story, assignedLabel) VALUES (?, ?)", (user_story, assigned_label))
|
41 |
+
conn.commit()
|
42 |
+
|
CSIT321Project-Info.txt
ADDED
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
**CSIT321 Project FYPS1-21**
|
2 |
+
## Our Refineverse Plugin URL on Hugging Face's Spaces
|
3 |
+
https://huggingface.co/spaces/FYP-S1-21/Refineverse_Plugin
|
4 |
+
|
5 |
+
## Our Source code Repository:
|
6 |
+
https://github.com/Nikosgo/FYP-S1-21-Refineverse.git
|
7 |
+
|
8 |
+
|
9 |
+
---
|
10 |
+
|
11 |
+
## IMPORTANT! Rules to follow during development:
|
12 |
+
1. Only Commit to your own branch/workspace under Source control.(Message..to commit on "your name")
|
13 |
+
2. Please DO NOT push or commit to "Main" branch (very Important)
|
14 |
+
3. Before doing anything with code, always select Pull From "Main" under Source Control
|
15 |
+
4. Make sure you are in your own branch/workspace before doing anything.
|
16 |
+
|
17 |
+
---
|
18 |
+
|
19 |
+
## SET UP
|
20 |
+
1. Install Vscode
|
21 |
+
2. Install XAMPP https://www.apachefriends.org/download.html
|
22 |
+
|
23 |
+
---
|
24 |
+
|
25 |
+
## In Vscode, before cloning repository:
|
26 |
+
1. Make sure to install Git https://git-scm.com/downloads (Ignore if Git already installed)
|
27 |
+
2. Install Git Extension Pack, Extension Pack for Java, Jira and Bitbucket(Atlassian Labs).
|
28 |
+
3. Install PHP Extensions.
|
29 |
+
|
30 |
+
## In Vscode, cloning repository:
|
31 |
+
1. In New Window, Select "Clone Git Repository..."
|
32 |
+
2. Copy & paste https://github.com/Nikosgo/FYP-S1-21-Refineverse.git in github url option.
|
33 |
+
3. Click and select "Clone From github". Do not select clone from URL!
|
34 |
+
4. Select local repository location as C:\xampp\htdocs (Xampp)
|
35 |
+
5. Prompt to login in to Atlassian Account, Do so.
|
36 |
+
|
37 |
+
## Setting Git Credentials and Access:
|
38 |
+
1. Select and Open New Terminal under Terminal Tab.
|
39 |
+
2. Type "cd" command to go root directory of your Git repository.
|
40 |
+
3. Type and run git config --global user.name "your Github username"
|
41 |
+
4. Type and run git config --global user.email "your Github email"
|
42 |
+
5. Type and run git config --global credential.helper wincred to store your Github credentials
|
43 |
+
6. Type and run git config --list to verify verify that your Git configuration is set up correctly
|
44 |
+
|
45 |
+
---
|
46 |
+
|
47 |
+
## Selecting Your Branch/Workspace
|
48 |
+
1. In left Side panel of screen, select Source Control
|
49 |
+
2. Select the 3 dots next to refresh icon and all the other icons.
|
50 |
+
3. Select Checkout to origin/yourname
|
51 |
+
|
52 |
+
---
|
53 |
+
|
54 |
+
## To be done each time before any coding to get most updated version of source codes
|
55 |
+
1. In left Side panel of screen, select Source Control
|
56 |
+
2. Select the 3 dots next to refresh icon and all the other icons.
|
57 |
+
3. Under "Pull,Push" select "Pull From..."
|
58 |
+
4. Choose "origin/main"
|
59 |
+
|
60 |
+
---
|
61 |
+
|
62 |
+
## How to push commit to your codes?
|
63 |
+
1. Under File tab, make sure "Autosave" is checked to automatically save your codes
|
64 |
+
2. Under Source Control, ensure message box shows "Message (Ctrl+Enter to commit on "yourname")
|
65 |
+
3. Below blue commit button, under Changes select the "+" sign to stage changes.
|
66 |
+
4. Write a message for the change and Select blue Commit Button.
|
67 |
+
5. Select Sync Changes to update local repository.
|
68 |
+
|
69 |
+
---
|
70 |
+
|
71 |
+
## Setting up a Python Virtual Environment (in VS Code)
|
72 |
+
To run/debug our project's NLP-related Python code, you must first set up a Python virtual environment.
|
73 |
+
Alternatively, you can install the libraries locally on your machine (not recommended).
|
74 |
+
**Important!** At the very least you must have Python installed locally on your machine and the Python extension enabled in VS Code.
|
75 |
+
The library installation commands listed here are meant for Windows machines. Do find the approriate commands for Linux/Mac yourself.
|
76 |
+
|
77 |
+
When running the virtual environment, you might run into an error intially when running the activate script due to Window's PowerShell. (Ignore if not using a Windows machine)
|
78 |
+
You can rectify this by changing the current execution policy for running scripts.
|
79 |
+
1. Open up Window's PowerShell (not command prompt!) **as an administrator**.
|
80 |
+
2. Use Get-ExecutionPolicy to check the current execution policy.
|
81 |
+
3. If it is "Restricted", set it to either "RemoteSigned" or "Unrestricted" using "Set-ExecutionPolicy RemoteSigned" or "Set-ExecutionPolicy Unrestricted".
|
82 |
+
4. You can change it back after you are done with the virtual environment using "Set-ExecutionPolicy Restricted".
|
83 |
+
|
84 |
+
Now you can open up VS Code to create the virutal environment.
|
85 |
+
1. In VS Code, use the shortcut "Ctrl+Shift+P" to open up the command palette.
|
86 |
+
2. Search for "Python: Create Environment..." and click it.
|
87 |
+
3. When asked to pick an enviroment type, choose "Venv".
|
88 |
+
4. When asked for a Python installation to use you should only have one option listed which is the Python version installed on your machine locally. Choose that option.
|
89 |
+
5. A notification will appear to show the progress of the environment creation. After creation, a folder will appear in your workplace automatically, and the Python intepreter should be changed automatically.
|
90 |
+
6. Verify if your Python has been changed to the new environment by looking at the bottom-right of your VS Code. It should say something like "Python 3.11.3 ('venv':venv)". If not, click on it and change it to the correct environment.
|
91 |
+
7. Now you can start installing the neccessary libraries by opening a new terminal window in VS Code. Navigate to the Terminal tab and choose "New Terminal". The new environment should be reflected in the terminal.
|
92 |
+
8. Install Pytorch (CPU): Pytorch (CPU): pip3 install torch torchvision torchaudio
|
93 |
+
9. Install Transformers: pip install transformers
|
94 |
+
11. These libraries are the bare minimum for running NLP code using HuggingFace's transformers. You can now try running some test code to verify.
|
95 |
+
|
96 |
+
To note:
|
97 |
+
By default, the virtual enviroment will not be detected as a new change/commit due to the automatically generated .gitignore file.
|
98 |
+
Please **do not** attempt to change this and upload your environment to GitHub as it may cause errors!
|
99 |
+
|
100 |
+
---
|
101 |
+
|
102 |
+
## Managing your Virtual Environment
|
103 |
+
As the path of the Python installation used to build the environment is user-specific, it cannot be shared.
|
104 |
+
Thus, the virtual environment must be maintained and installed properly by yourself.
|
105 |
+
Feel free to rename your virtual environment anything you want.
|
106 |
+
|
107 |
+
If you wish to install more libraries to test/debug new features (such as Flask or Beautiful Soup) go ahead.
|
108 |
+
However, do communicate with the team if a new library is being used to enable team members to run your code as well.
|
109 |
+
The final required Python libraries for our project will be included in a requirements.txt file.
|
110 |
+
If for some reason you encounter issues such as library conflicts or errors, feel free to remove your environment and follow the steps to create a new one again.
|
111 |
+
|
112 |
+
---
|
113 |
+
|
114 |
+
## Installing your libraries
|
115 |
+
1. Ensure that you have already setup and activated your virtual environment.
|
116 |
+
2. In the VsCode terminal, type the command "pip install -r requirements.txt" and click enter to allow the system to automatically install all necessary libraries that have been defined in the requirements.txt file in order to run the application.
|
117 |
+
|
118 |
+
---
|
119 |
+
|
120 |
+
## Creating or Saving a requirements.txt for library installation
|
121 |
+
1. Ensure that you have already setup and activated your virtual environment.
|
122 |
+
2. Delete any existing requirements.txt file as the command does not overwrite the text file.
|
123 |
+
2. In the VsCode terminal, type the command "pip freeze > requirements.txt" and click enter to save all libraries that have already been installed into the requirements.txt file.
|
GenerationFeature.py
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re # Python's built-in library for regular expressions (or Regex)
|
2 |
+
import sqlite3
|
3 |
+
from flask import g
|
4 |
+
from transformers import pipeline, set_seed
|
5 |
+
|
6 |
+
# Main function of the generation feature. Performs text generation!
|
7 |
+
def generate(Entered_story):
|
8 |
+
|
9 |
+
# Check if the input is empty
|
10 |
+
if not Entered_story.strip():
|
11 |
+
raise ValueError("Empty input!")
|
12 |
+
|
13 |
+
# Validate that the input is in the correct format
|
14 |
+
if not validate_story(Entered_story):
|
15 |
+
raise ValueError("Incorrect format!")
|
16 |
+
|
17 |
+
# Set the pipeline to use the correct NLP type and model
|
18 |
+
generator = pipeline('text-generation', model='gpt2')
|
19 |
+
|
20 |
+
# Take note: The max_length & min_length variables refer to the OUTPUT length!
|
21 |
+
set_seed(42)
|
22 |
+
generated_text = generator(Entered_story, max_length=30, num_return_sequences=5)
|
23 |
+
|
24 |
+
generated_text = generated_text[0]['generated_text']
|
25 |
+
|
26 |
+
return generated_text
|
27 |
+
|
28 |
+
# User Input Format Validation Function
|
29 |
+
def validate_story(Entered_story):
|
30 |
+
pattern = r'As a (?P<role>[^,.]+), I want to (?P<goal>[^,.]+)(?:,|.)+\s*so that' #Follows the normal structure, but allows anything after 'so that'
|
31 |
+
match = re.search(pattern, Entered_story, flags=re.DOTALL)
|
32 |
+
return bool(match)
|
33 |
+
|
34 |
+
# Function to grab all contents in the "TextGeneration" table (except for unique ids)
|
35 |
+
def getTextGenContents():
|
36 |
+
db = getattr(g, '_database', None) # Gets the _database attribute from the 'g' object. If it does not exist, returns 'None'
|
37 |
+
if db is None:
|
38 |
+
db = g._database = sqlite3.connect('Refineverse.db') # If db is None, create a new connection for db and g._database.
|
39 |
+
cursor = db.cursor() # Creates a cursor object to handle data
|
40 |
+
cursor.execute("SELECT userStory, generatedStory FROM TextGeneration") # The cursor executes the query
|
41 |
+
rows = cursor.fetchall() # Stores the results of fetchall() into a variable
|
42 |
+
return rows
|
43 |
+
|
44 |
+
# Function to insert a new row into the "TextGeneration" table
|
45 |
+
def insertTextGenRow( Entered_story, generatedStory):
|
46 |
+
with sqlite3.connect('Refineverse.db') as conn: # 'With' will automatically take care of closing and opening the connection
|
47 |
+
cursor = conn.cursor()
|
48 |
+
cursor.execute("INSERT INTO TextGeneration (userStory, generatedStory) VALUES (?, ?)", (Entered_story, generatedStory))
|
49 |
+
conn.commit()
|
README.md
CHANGED
@@ -1,10 +1,13 @@
|
|
1 |
---
|
2 |
-
title: Refineverse
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
-
sdk:
|
|
|
|
|
7 |
pinned: false
|
8 |
---
|
9 |
|
10 |
-
|
|
|
|
1 |
---
|
2 |
+
title: Refineverse
|
3 |
+
emoji: 💻
|
4 |
+
colorFrom: blue
|
5 |
+
colorTo: gray
|
6 |
+
sdk: gradio
|
7 |
+
python_version: 3.11.3
|
8 |
+
app_file: Refineverse.py
|
9 |
pinned: false
|
10 |
---
|
11 |
|
12 |
+
This is to host our plugin online on Hugging Face.
|
13 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
Refineverse.db
ADDED
Binary file (24.6 kB). View file
|
|
Refineverse.py
ADDED
@@ -0,0 +1,225 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Plugin will run on this file & Strictly only contains codes for routing between files!
|
2 |
+
from flask import Flask, render_template, request, flash, g
|
3 |
+
from TextSummarizationFeature import summarize, getTextSumContents, insertTextSumRow
|
4 |
+
from BreakdownFeature import breakdown, getBreakdownContents, insertBreakdownRow
|
5 |
+
from TranslationFeature import translate_text, switch, getTranslatedContents, insertTranslationRow
|
6 |
+
from GenerationFeature import generate, getTextGenContents, insertTextGenRow
|
7 |
+
|
8 |
+
app = Flask(__name__)
|
9 |
+
app.secret_key = 'refineverseAdmin' # Used to encrypt cookies & sessions
|
10 |
+
|
11 |
+
# Routing to Main Dashboard/homepage file
|
12 |
+
@app.route('/')
|
13 |
+
def index():
|
14 |
+
return render_template('RefineverseDashboardUI.html')
|
15 |
+
|
16 |
+
# Routing to text summarization file
|
17 |
+
@app.route('/text_summarization', methods=["POST", "GET"])
|
18 |
+
def text_summarization():
|
19 |
+
if request.method == "POST":
|
20 |
+
try:
|
21 |
+
# Grab the user story text from the textarea in html form
|
22 |
+
Entered_story = request.form["input_text"]
|
23 |
+
|
24 |
+
# The results are stored into a dictionary variable
|
25 |
+
summarizedStory = summarize(Entered_story)
|
26 |
+
|
27 |
+
flash("Your user story has been summarized!") # Displays a success message using flash, which is part of the Flask framework
|
28 |
+
|
29 |
+
# Insert into TextSummarization table in Refineverse.db
|
30 |
+
insertTextSumRow(Entered_story, summarizedStory)
|
31 |
+
|
32 |
+
# Render and display summarized user story
|
33 |
+
return render_template('TextSummarizationUI.html', summarizedStory=summarizedStory)
|
34 |
+
|
35 |
+
# Exception handling messages for specific errors
|
36 |
+
except ValueError as e:
|
37 |
+
if str(e) == "Empty input!":
|
38 |
+
flash("The input text cannot be empty! Please enter a user story.", 'error')
|
39 |
+
return render_template('TextSummarizationUI.html')
|
40 |
+
elif str(e) == "Incorrect format!":
|
41 |
+
flash("Incorrect user story format! Please enter in the right format.", 'error')
|
42 |
+
return render_template('TextSummarizationUI.html')
|
43 |
+
elif str(e) == "Invalid length!":
|
44 |
+
flash("Your inputted user story is too short to summarize. Please enter a longer story!", 'error')
|
45 |
+
return render_template('TextSummarizationUI.html')
|
46 |
+
else: # As a final resort, simply print out the error name
|
47 |
+
flash("An error of type '{}' occurred: {}".format(type(e).__name__, str(e)), 'error')
|
48 |
+
return render_template('TextSummarizationUI.html')
|
49 |
+
|
50 |
+
except KeyError:
|
51 |
+
flash("Please enter a valid user story!")
|
52 |
+
return render_template('TextSummarizationUI.html')
|
53 |
+
|
54 |
+
# Catch-all exception handling
|
55 |
+
except Exception as e:
|
56 |
+
flash("An error of type '{}' occurred: {}".format(type(e).__name__, str(e)), 'error')
|
57 |
+
return render_template('TextSummarizationUI.html')
|
58 |
+
|
59 |
+
else:
|
60 |
+
return render_template('TextSummarizationUI.html')
|
61 |
+
|
62 |
+
# Routing to summarization table file
|
63 |
+
@app.route('/summarization_table')
|
64 |
+
def summarization_table():
|
65 |
+
# Get the summarization data from the database
|
66 |
+
summarizations = getTextSumContents()
|
67 |
+
|
68 |
+
# Render the summarization data as an HTML table
|
69 |
+
return render_template('SummarizationTable.html', summarizations=summarizations)
|
70 |
+
|
71 |
+
# Routing to Project Task Breakdown file
|
72 |
+
@app.route("/project_breakdown", methods=["POST", "GET"]) # This tells flask the route to get to the page
|
73 |
+
def project_breakdown():
|
74 |
+
if request.method == "POST": # POST occurs when submitting a form, as specified in the HTML file
|
75 |
+
try:
|
76 |
+
# Grab the user story contents
|
77 |
+
userStory = request.form["user-story-text"]
|
78 |
+
|
79 |
+
# The results are stored into a dictionary variable
|
80 |
+
processedLabel = breakdown(userStory)
|
81 |
+
|
82 |
+
# Display success popup message
|
83 |
+
flash("Your user story has been allocated as a " + processedLabel + " task!")
|
84 |
+
|
85 |
+
insertBreakdownRow(userStory, processedLabel) # Inserts data into the Breakdown table
|
86 |
+
rows = getBreakdownContents() # Grab all contents inside the Breakdown table
|
87 |
+
|
88 |
+
return render_template('ProjectBreakdownUI.html', rows=rows)
|
89 |
+
|
90 |
+
# Exception handling messages for specific errors
|
91 |
+
except KeyError:
|
92 |
+
flash("Please enter a valid user story!", 'error')
|
93 |
+
rows = getBreakdownContents()
|
94 |
+
return render_template('ProjectBreakdownUI.html', row=rows)
|
95 |
+
|
96 |
+
# Catch-all exception handling
|
97 |
+
except Exception as e:
|
98 |
+
flash("An error of type '{}' occurred: {}".format(type(e).__name__, str(e)), 'error')
|
99 |
+
rows = getBreakdownContents()
|
100 |
+
return render_template('ProjectBreakdownUI.html', rows=rows)
|
101 |
+
|
102 |
+
else: # For "GET" scenarios (loading the page, etc.)
|
103 |
+
rows = getBreakdownContents() # To always display the table, we must grab the contents of Breakdown every time the page loads
|
104 |
+
return render_template('ProjectBreakdownUI.html', rows=rows)
|
105 |
+
|
106 |
+
# Routing to Translation file
|
107 |
+
@app.route('/language_translation', methods=["POST", "GET"])
|
108 |
+
def language_translation():
|
109 |
+
if request.method == "POST":
|
110 |
+
try:
|
111 |
+
# Grab all relevant information for processing
|
112 |
+
input_text = request.form['input'] # Grab user text input
|
113 |
+
|
114 |
+
# Grab source language code
|
115 |
+
source_language = request.form['source_language']
|
116 |
+
|
117 |
+
# Grab target language code
|
118 |
+
target_language = request.form['target_language']
|
119 |
+
|
120 |
+
# Generate translated text using custom translation function
|
121 |
+
translatedStory = translate_text(input_text, source_language, target_language)
|
122 |
+
|
123 |
+
# Display success popup message
|
124 |
+
flash("Your user story has been translated to " + switch(target_language) + " !")
|
125 |
+
|
126 |
+
# Insert into Translation table in Refineverse.db
|
127 |
+
insertTranslationRow(input_text, translatedStory)
|
128 |
+
|
129 |
+
# Display the page
|
130 |
+
return render_template('LanguageTranslationUI.html', input_text=input_text, translatedStory=translatedStory)
|
131 |
+
|
132 |
+
# Exception handling messages for specific errors
|
133 |
+
except ValueError as e:
|
134 |
+
if str(e) == "Empty input!":
|
135 |
+
flash("The input text cannot be empty! Please enter a user story.", 'error')
|
136 |
+
return render_template('LanguageTranslationUI.html')
|
137 |
+
elif str(e) == "Incorrect format!":
|
138 |
+
flash("Unable to translate your user story. Please enter in the correct format.", 'error')
|
139 |
+
return render_template('LanguageTranslationUI.html')
|
140 |
+
else: # As a final resort, simply print out the error name
|
141 |
+
flash("An error of type '{}' occurred: {}".format(type(e).__name__, str(e)), 'error')
|
142 |
+
return render_template('LanguageTranslationUI.html')
|
143 |
+
|
144 |
+
# Catch-all exception handling
|
145 |
+
except Exception as e:
|
146 |
+
flash("An error of type '{}' occurred: {}".format(type(e).__name__, str(e)), 'error')
|
147 |
+
return render_template('LanguageTranslationUI.html')
|
148 |
+
|
149 |
+
else:
|
150 |
+
return render_template('LanguageTranslationUI.html')
|
151 |
+
|
152 |
+
# Routing to translation table file
|
153 |
+
@app.route('/translation_table')
|
154 |
+
def translation_data():
|
155 |
+
# Get the translation data from the database
|
156 |
+
translations = getTranslatedContents()
|
157 |
+
|
158 |
+
# Render the translation data as an HTML table
|
159 |
+
return render_template('TranslationTable.html', translations=translations)
|
160 |
+
|
161 |
+
# Routing to text summarization file
|
162 |
+
@app.route('/text_generation', methods=["POST", "GET"])
|
163 |
+
def text_generation():
|
164 |
+
if request.method == "POST":
|
165 |
+
try:
|
166 |
+
# Grab the user story text from the textarea in html form
|
167 |
+
Entered_story = request.form["input_text"]
|
168 |
+
|
169 |
+
# The results are stored into a dictionary variable
|
170 |
+
generatedStory = generate(Entered_story)
|
171 |
+
|
172 |
+
# Display a success message for the user
|
173 |
+
flash("Your user story has been generated!")
|
174 |
+
|
175 |
+
# Insert into TextGeneration table in Refineverse.db
|
176 |
+
insertTextGenRow(Entered_story, generatedStory)
|
177 |
+
|
178 |
+
# Render and display summarized user story
|
179 |
+
return render_template('TextGenerationUI.html', generatedStory=generatedStory)
|
180 |
+
|
181 |
+
# Exception handling messages for specific errors
|
182 |
+
except ValueError as e:
|
183 |
+
if str(e) == "Empty input!":
|
184 |
+
flash("The input text cannot be empty! Please enter a user story.", 'error')
|
185 |
+
return render_template('TextGenerationUI.html')
|
186 |
+
elif str(e) == "Incorrect format!":
|
187 |
+
flash("Incorrect user story format! Please enter in the right format.", 'error')
|
188 |
+
return render_template('TextGenerationUI.html')
|
189 |
+
else: # As a final resort, simply print out the error name
|
190 |
+
flash("An error of type '{}' occurred: {}".format(type(e).__name__, str(e)), 'error')
|
191 |
+
return render_template('TextGenerationUI.html')
|
192 |
+
|
193 |
+
except KeyError:
|
194 |
+
flash("Please enter a valid user story!")
|
195 |
+
return render_template('TextGenerationUI.html')
|
196 |
+
|
197 |
+
# Catch-all exception handling
|
198 |
+
except Exception as e:
|
199 |
+
flash("An error of type '{}' occurred: {}".format(type(e).__name__, str(e)), 'error')
|
200 |
+
return render_template('TextGenerationUI.html')
|
201 |
+
|
202 |
+
else:
|
203 |
+
return render_template('TextGenerationUI.html')
|
204 |
+
|
205 |
+
# Routing to generation table file
|
206 |
+
@app.route('/generation_table')
|
207 |
+
def generation_table():
|
208 |
+
# Get the generation data from the database
|
209 |
+
generations = getTextGenContents()
|
210 |
+
|
211 |
+
# Render the generation data as an HTML table
|
212 |
+
return render_template('GenerationTable.html', generations=generations)
|
213 |
+
|
214 |
+
# Used when the application is torn down
|
215 |
+
# Its purpose is to close the database connection if it has not been closed
|
216 |
+
@app.teardown_appcontext
|
217 |
+
def close_connection(exception):
|
218 |
+
db = getattr(g, '_database', None)
|
219 |
+
if db is not None:
|
220 |
+
db.close() # Closes the database connection
|
221 |
+
|
222 |
+
# Initialise the app
|
223 |
+
if __name__ == '__main__':
|
224 |
+
app.run(host="0.0.0.0", port=7860) # For HF hosting
|
225 |
+
#app.run(debug=False) # can set to True/False for local testing
|
TextSummarizationFeature.py
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re # Python's built-in library for regular expressions (or Regex)
|
2 |
+
import sqlite3
|
3 |
+
from flask import g
|
4 |
+
from transformers import pipeline
|
5 |
+
|
6 |
+
# Main function of the summarization feature. Performs summarization!
|
7 |
+
def summarize(Entered_story):
|
8 |
+
|
9 |
+
# Check if the input is empty
|
10 |
+
if not Entered_story.strip():
|
11 |
+
raise ValueError("Empty input!")
|
12 |
+
|
13 |
+
# Validate that the input is in the correct format
|
14 |
+
if not validate_story(Entered_story):
|
15 |
+
raise ValueError("Incorrect format!")
|
16 |
+
|
17 |
+
# Before we do anything, make sure the input is long enough for summarization.
|
18 |
+
if len(Entered_story) < 200:
|
19 |
+
raise ValueError("Invalid length!")
|
20 |
+
|
21 |
+
# Set the pipeline to use the correct NLP type and model
|
22 |
+
summarizer = pipeline("summarization", model="facebook/bart-large-cnn")
|
23 |
+
|
24 |
+
# Take note: The max_length & min_length variables refer to the OUTPUT length!
|
25 |
+
summary = summarizer(Entered_story, max_length=100, min_length=30, do_sample=False)[0]["summary_text"]
|
26 |
+
|
27 |
+
return summary
|
28 |
+
|
29 |
+
# User Input Format Validation Function for English only
|
30 |
+
def validate_story(Entered_story):
|
31 |
+
pattern = r'As a (?P<role>[^,.]+), I want to (?P<goal>[^,.]+)(,|.)+so that (?P<benefit>.+)'
|
32 |
+
match = re.search(pattern, Entered_story, flags=re.DOTALL)
|
33 |
+
return bool(match)
|
34 |
+
|
35 |
+
# Function to grab all contents in the "TextSummarization" table (except for unique ids)
|
36 |
+
def getTextSumContents():
|
37 |
+
db = getattr(g, '_database', None) # Gets the _database attribute from the 'g' object. If it does not exist, returns 'None'
|
38 |
+
if db is None:
|
39 |
+
db = g._database = sqlite3.connect('Refineverse.db') # If db is None, create a new connection for db and g._database.
|
40 |
+
cursor = db.cursor() # Creates a cursor object to handle data
|
41 |
+
cursor.execute("SELECT Entered_story, summary FROM TextSummarization") # The cursor executes the query
|
42 |
+
rows = cursor.fetchall() # Stores the results of fetchall() into a variable
|
43 |
+
return rows
|
44 |
+
|
45 |
+
# Function to insert a new row into the "TextSummarization" table
|
46 |
+
def insertTextSumRow( Entered_story, summary):
|
47 |
+
with sqlite3.connect('Refineverse.db') as conn: # 'With' will automatically take care of closing and opening the connection
|
48 |
+
cursor = conn.cursor()
|
49 |
+
cursor.execute("INSERT INTO TextSummarization (Entered_story, summary) VALUES (?, ?)", (Entered_story, summary))
|
50 |
+
conn.commit()
|
TranslationFeature.py
ADDED
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re
|
2 |
+
import sqlite3
|
3 |
+
from flask import g
|
4 |
+
from transformers import M2M100ForConditionalGeneration, M2M100Tokenizer
|
5 |
+
|
6 |
+
model = M2M100ForConditionalGeneration.from_pretrained("facebook/m2m100_1.2B") # Setting the model to use
|
7 |
+
tokenizer = M2M100Tokenizer.from_pretrained("facebook/m2m100_1.2B") # Setting the tokenizer to use
|
8 |
+
|
9 |
+
# Main function of the translation feature. Performs translation!
|
10 |
+
def translate_text(input_text, source_language, target_language):
|
11 |
+
|
12 |
+
# Grabs the source language to be used in the tokenizer
|
13 |
+
tokenizer.src_lang = source_language
|
14 |
+
|
15 |
+
# Check if the input is empty
|
16 |
+
if not input_text.strip():
|
17 |
+
raise ValueError("Empty input!")
|
18 |
+
|
19 |
+
# Validate that the input is in the correct format
|
20 |
+
if not validate_input(input_text):
|
21 |
+
raise ValueError("Incorrect format!")
|
22 |
+
|
23 |
+
# Creates encoded text
|
24 |
+
encoded_text = tokenizer(input_text, return_tensors="pt")
|
25 |
+
|
26 |
+
# Generates new tokens using encoded text from source language
|
27 |
+
generated_tokens = model.generate(**encoded_text, forced_bos_token_id=tokenizer.get_lang_id(target_language), max_new_tokens=512)
|
28 |
+
|
29 |
+
# Decode generated tokens to display translated text
|
30 |
+
translated_text = tokenizer.batch_decode(generated_tokens, skip_special_tokens=True)[0]
|
31 |
+
|
32 |
+
return translated_text
|
33 |
+
|
34 |
+
# Helper function for displaying appropriate language names in flash messages
|
35 |
+
# Note: Python does not have a built-in switch function, so this is just a rough implementation of the logic
|
36 |
+
def switch(lang):
|
37 |
+
if lang == "en":
|
38 |
+
return "English"
|
39 |
+
elif lang == "zh":
|
40 |
+
return "Chinese"
|
41 |
+
elif lang == "ms":
|
42 |
+
return "Malay"
|
43 |
+
elif lang == "ta":
|
44 |
+
return "Tamil"
|
45 |
+
elif lang == "th":
|
46 |
+
return "Thai"
|
47 |
+
|
48 |
+
# User Input Format Validation Function for all 4 languages
|
49 |
+
def validate_input(input_text):
|
50 |
+
|
51 |
+
# Pattern for English language
|
52 |
+
pattern_en = r'As a (?P<role>[^,.]+), I want to (?P<goal>[^,.]+)(,|.)+so that (?P<benefit>.+)'
|
53 |
+
|
54 |
+
# Pattern for Chinese language
|
55 |
+
pattern_zh = r'作为(?P<role>[^,.]+),我想要(?P<goal>[^,.]+)(,|。)+以便(?P<benefit>.+)'
|
56 |
+
|
57 |
+
# Pattern for Malay language
|
58 |
+
pattern_ms = r'Sebagai(?P<role>[^,.]+), saya mahu(?P<goal>[^,.]+)(,|.)+supaya(?P<benefit>.+)'
|
59 |
+
|
60 |
+
# Pattern for Tamil language
|
61 |
+
pattern_ta = r'என(?P<role>[^,.]+) எனக்கு வேண்டும்(?P<goal>[^,.]+)(,|.)+அதனால்(?P<benefit>.+) பயன்படுத்தி வைக்கும்'
|
62 |
+
|
63 |
+
# Pattern for Thai language
|
64 |
+
pattern_th = r'ในฐานะ(?P<role>[^,.]+) ฉันต้องการ(?P<goal>[^,.]+)(,|.)+เพื่อที่ฉัน(?P<benefit>.+)'
|
65 |
+
|
66 |
+
# Try each pattern to see if there is a match
|
67 |
+
match_en = re.search(pattern_en, input_text, flags=re.DOTALL)
|
68 |
+
match_zh = re.search(pattern_zh, input_text, flags=re.DOTALL)
|
69 |
+
match_ms = re.search(pattern_ms, input_text, flags=re.DOTALL)
|
70 |
+
match_ta = re.search(pattern_ta, input_text, flags=re.DOTALL)
|
71 |
+
match_th = re.search(pattern_th, input_text, flags=re.DOTALL)
|
72 |
+
|
73 |
+
# Return True if at least one pattern matches, otherwise False
|
74 |
+
return bool(match_en or match_zh or match_ms or match_ta or match_th)
|
75 |
+
|
76 |
+
# Function to grab all contents in the "Translation" table (except for unique ids)
|
77 |
+
def getTranslatedContents():
|
78 |
+
db = getattr(g, '_database', None) # Gets the _database attribute from the 'g' object. If it does not exist, returns 'None'
|
79 |
+
if db is None:
|
80 |
+
db = g._database = sqlite3.connect('Refineverse.db') # If db is None, create a new connection for db and g._database
|
81 |
+
cursor = db.cursor() # Creates a cursor object to handle data
|
82 |
+
cursor.execute("SELECT input_text, translated_text FROM Translation") # The cursor executes the query
|
83 |
+
rows = cursor.fetchall() # Stores the results of fetchall() into a variable
|
84 |
+
return rows
|
85 |
+
|
86 |
+
# Function to insert a new row into the "Translation" table
|
87 |
+
def insertTranslationRow(input_text, translated_text):
|
88 |
+
with sqlite3.connect('Refineverse.db') as conn: # 'With' will automatically take care of closing and opening the connection
|
89 |
+
cursor = conn.cursor()
|
90 |
+
cursor.execute("INSERT INTO Translation (input_text, translated_text) VALUES (?, ?)", (input_text, translated_text))
|
91 |
+
conn.commit()
|
createDatabase.py
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# This file is used to create an SQLite database.
|
2 |
+
# If adding more tables, you should delete the exising databases, or alter the code here to drop the pre-exising tables first.
|
3 |
+
import sqlite3
|
4 |
+
|
5 |
+
# Establishes a connection to the specified DB. If the DB does not exist, it creates a new one.
|
6 |
+
connection = sqlite3.connect("Refineverse.db")
|
7 |
+
cursor = connection.cursor() # A cursor object that is used to handle data.
|
8 |
+
|
9 |
+
# Creating the Breakdown table
|
10 |
+
cursor.execute('''
|
11 |
+
CREATE TABLE IF NOT EXISTS Breakdown (
|
12 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
13 |
+
user_story TEXT,
|
14 |
+
assignedLabel TEXT
|
15 |
+
)
|
16 |
+
''')
|
17 |
+
|
18 |
+
# Creating the TextSummarization table
|
19 |
+
cursor.execute('''
|
20 |
+
CREATE TABLE IF NOT EXISTS TextSummarization (
|
21 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
22 |
+
Entered_story TEXT,
|
23 |
+
summary TEXT
|
24 |
+
)
|
25 |
+
''')
|
26 |
+
|
27 |
+
# Creating the Translation table
|
28 |
+
cursor.execute('''
|
29 |
+
CREATE TABLE IF NOT EXISTS Translation (
|
30 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
31 |
+
input_text TEXT,
|
32 |
+
translated_text TEXT
|
33 |
+
)
|
34 |
+
''')
|
35 |
+
|
36 |
+
# Creating the TextGeneration table
|
37 |
+
cursor.execute('''
|
38 |
+
CREATE TABLE IF NOT EXISTS TextGeneration (
|
39 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
40 |
+
userStory TEXT,
|
41 |
+
generatedStory TEXT
|
42 |
+
)
|
43 |
+
''')
|
44 |
+
|
45 |
+
connection.close() # Closes the connection
|
requirements.txt
ADDED
Binary file (1.17 kB). View file
|
|