Commit ca28827d authored by Ekanayake P.M.D.P IT18013610's avatar Ekanayake P.M.D.P IT18013610

Merge branch 'it18013610' into 'master'

It18013610

See merge request !10
parents 70363aeb 79c3cb92
......@@ -47,7 +47,6 @@ exports.requestAutomatedAnswer = async (req, res, next) => {
*/
exports.getAutomatedAnswer = async (req, res, next) => {
const { qid } = req.params;
console.log('answer is here' + qid);
try {
const answer = await AutomatedAnswer.findOne({ question: qid });
if (!answer) {
......
......@@ -44,8 +44,8 @@ exports.createQuestion = async (req, res, next) => {
pythonScript.stdout.on('data', (data) => {
console.log('DATA FROM PYTHON IS HERE' + data);
});
pythonScript.stdout.on('end', function () {
console.log('PYTHON SCRIPT HAS BEEN ENDED');
pythonScript.stderr.on('data', (data) => {
console.log('PYTHON SCRIPT ERROR : ' + data);
});
} catch (error) {
next(error);
......
......@@ -2,7 +2,6 @@ const app = require('./app');
const mongoose = require('mongoose');
const config = require('./config');
const connect = (url) => {
return mongoose.connect(url, config.db.options);
};
......
......@@ -62,7 +62,15 @@ const AutomatedAnswerSchema = mongoose.Schema({
{
type: String
}
]
],
isLoading: {
type: Boolean,
default: true
},
isComplete: {
type: Boolean,
default: false
}
});
module.exports = mongoose.model('AutomatedAnswer', AutomatedAnswerSchema);
{
"name": "stackoverflow-api",
"name": "probexpert-be",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
......
{
"name": "stackoverflow-api",
"name": "probexpert-be",
"version": "1.0.0",
"description": "Stackoverflow Clone Server",
"description": "Probexpert backend",
"main": "index.js",
"scripts": {
"start": "node index.js",
......@@ -14,7 +14,7 @@
"Rest",
"MERN"
],
"author": "Salih Ozdemir",
"author": "Dasun Ekanayake",
"license": "ISC",
"dependencies": {
"bcryptjs": "^2.4.3",
......
import spacy
from collections import Counter
from string import punctuation
class keywords:
"""
A class to extract technical related keywords from a text
"""
def __init__(self, text):
"""
Initialize a keyword object
"""
self.text = text
self.spacy_nlp = spacy.load("en_core_web_lg")
def extract(self):
"""
Extract keywords from the text
"""
result = []
pos_tag = ["PROPN", "ADJ", "NOUN"]
doc = self.spacy_nlp(self.text.lower())
for token in doc:
if (
token.text in self.spacy_nlp.Defaults.stop_words
or token.text in punctuation
):
continue
if token.pos_ in pos_tag:
result.append(token.text)
return result
import certifi
ca = certifi.where()
DATABASE_URL_PROD = "mongodb+srv://admin2:admin12345@cluster0.u4vl4.mongodb.net/production?retryWrites=true&w=majority"
DATABASE_URL_DEV = "mongodb+srv://admin:admin1234@cluster0.u4vl4.mongodb.net/test?retryWrites=true&w=majority"
......@@ -15,7 +18,7 @@ def get_database():
CONNECTION_STRING = ENV == "DEV" and DATABASE_URL_DEV or DATABASE_URL_PROD
# Create a connection using MongoClient. You can import MongoClient or use pymongo.MongoClient
client = MongoClient(CONNECTION_STRING)
client = MongoClient(CONNECTION_STRING, tlsCAFile=ca)
# Create the database for our example (we will use the same database throughout the tutorial
return ENV == "DEV" and client["test"] or client["production"]
......@@ -3,15 +3,16 @@ from search_engine_parser import GoogleSearch
import re
class Github:
class GithubData:
"""
A class to manage the Github API.
"""
def __init__(self):
def __init__(self, title):
"""
Initialize the Github API.
"""
print(title)
def get_github_resources(self, query):
"""
......
......@@ -2,7 +2,7 @@ from youtube import Youtube
from Medium import Medium
from Dev import DevTo
from stof import STOF
from Github import Github
from github import GithubData
import sys
from database import get_database
......@@ -17,7 +17,8 @@ def saveAnswer(ans_id, stackoverflow, videos, medium_r, dev_r, github_r):
{"_id": ObjectId(ans_id)},
{
"$set": {
"loading":False,
"isLoading": False,
"isComplete": True,
"youtube": videos,
"stackoverflow": stackoverflow,
"medium_articles": medium_r.get("blogs", []),
......@@ -29,18 +30,7 @@ def saveAnswer(ans_id, stackoverflow, videos, medium_r, dev_r, github_r):
}
},
)
print(
{
"youtube": videos,
"stackoverflow": stackoverflow,
"medium_articles": medium_r["blogs"],
"dev_articles": dev_r["blogs"],
"medium_resources": medium_r["resources"],
"dev_resources": dev_r["resources"],
"github_repos": github_r["repos"],
"github_links": github_r["links"],
}
)
except NameError as err:
print("ERRORRR")
print(err)
......@@ -48,7 +38,7 @@ def saveAnswer(ans_id, stackoverflow, videos, medium_r, dev_r, github_r):
if __name__ == "__main__":
# title = input("Enter question title: ")
title = sys.argv[1]
title = sys.argv[1]
# "what are the benefits of using java for mobile app development over flutter"
tags = sys.argv[2] # ["flutter","java"]
AUTO_ANS_ID = sys.argv[3] # "611feaff2c4db730e56d78e8"
......@@ -57,7 +47,7 @@ if __name__ == "__main__":
medium = Medium(title, tags)
devto = DevTo(title, tags)
youtube = Youtube(title, tags)
github = Github()
github = GithubData(title)
ans = stack.searchQuestion()
medium_articels = medium.getMediumArticles()
dev_articles = devto.get_dev_articles()
......
......@@ -73,7 +73,14 @@ class STOF:
# print(soup.prettify().encode("utf-8"))
return answer
def calculateAccuracy(self):
"""
Compare the user's question with stackoverflow question and calculate the accuracy
"""
\ No newline at end of file
def calculateAccuracy(self):
"""
Compare the user's question with stackoverflow question and calculate the accuracy
"""
pass
def get_clean_url(url):
import re
pattern = r"^.*?\&sa="
return re.match(pattern, url).group(0)
......@@ -27,3 +27,4 @@ class Youtube:
videos.append(i["link"])
print(i["link"])
return videos
\ No newline at end of file
......@@ -9,6 +9,7 @@ import styles from './automated-answer-container.module.css'
const AutomatedAnswerContainer = ({ question_id }) => {
const [loading, setLoading] = useState(true)
const [answer, setAnswer] = useState()
const [isAnswerGenerating, setIsAnswertGenerating] = useState(false)
const [error, setError] = useState({
status: '',
action: '',
......@@ -20,8 +21,16 @@ const AutomatedAnswerContainer = ({ question_id }) => {
const fetchAutomatedAnswer = async () => {
try {
const response = await publicFetch.get(`automatedanswer/${question_id}`)
if (response.status == 200) setAnswer(response.data)
else setServerError({ response })
if (response.status == 200) {
console.log('RESPONSE IS HERE AND IT IS')
const { isLoading, isComplete } = response.data.automatedanswer
if (isLoading && !isComplete) {
setIsAnswertGenerating(true)
setServerError(102)
} else {
setAnswer(response.data)
}
} else setServerError({ response })
} catch (error) {
setServerError(error)
}
......@@ -31,7 +40,14 @@ const AutomatedAnswerContainer = ({ question_id }) => {
}, [question_id])
const setServerError = (error) => {
if (error.response.status == 404) {
if (error == 102) {
setError({
status: '102',
action: 'loading',
button: 'Answer is generatingg',
message: 'Automated answer 🤖 is generating in the backend '
})
} else if (error.response.status == 404) {
setError({
status: '404',
action: 'create',
......@@ -48,6 +64,30 @@ const AutomatedAnswerContainer = ({ question_id }) => {
}
}
const getBody = () => {
if (loading) {
return <Spinner className={styles.spinner} />
} else if (isAnswerGenerating) {
return (
<NoAutomatedAnswer
question_id={question_id}
error={error}
isGenerating={true}
/>
)
} else if (answer != null) {
return <AutomatedAnswer automatedAnswer={answer.automatedanswer} />
} else {
return (
<NoAutomatedAnswer
question_id={question_id}
error={error}
isGenerating={false}
/>
)
}
}
return (
<div className={styles.container}>
<div className={styles.header}>
......@@ -55,15 +95,7 @@ const AutomatedAnswerContainer = ({ question_id }) => {
<h2>Automated Answer</h2>
</div>
</div>
<div className={styles.wrapper}>
{loading ? (
<Spinner className={styles.spinner} />
) : answer != null ? (
<AutomatedAnswer automatedAnswer={answer.automatedanswer} />
) : (
<NoAutomatedAnswer question_id={question_id} error={error} />
)}
</div>
<div className={styles.wrapper}>{getBody()}</div>
</div>
)
}
......
import React from 'react'
import BlogsWrapper from '../blogs-wrapper/BlogsWrapper'
import StackOverflowAnswer from '../stof-answer'
import YoutubeVideoWrapper from '../youtube-videos/YoutubeVideoWrapper'
......@@ -18,6 +19,18 @@ const AutomatedAnswer = ({ automatedAnswer }) => {
) : (
<h1>No youtubes found for this question</h1>
)}
<BlogsWrapper
source="Medium"
articles={automatedAnswer.medium_articles}
resources={automatedAnswer.medium_resources}
/>
<BlogsWrapper
source="Dev.to"
articles={automatedAnswer.dev_articles}
resources={automatedAnswer.dev_resources}
/>
</>
)
}
......
import React from 'react'
import React, { useContext } from 'react'
import { FetchContext } from '../../../store/fetch'
import styles from './no-automated-answer.module.css'
const NoAutomatedAnswer = ({ question_id, error }) => {
import { Spinner } from '../../icons'
const NoAutomatedAnswer = ({ question_id, error, isGenerating }) => {
const { authAxios } = useContext(FetchContext)
const { status, action, button, message } = error
return (
<div className={styles.no_automated_answer}>
<h1 className={styles.title}>{message}</h1>
<button className={styles.generate_btn}>{button}</button>
{isGenerating ? (
<Spinner className={styles.spinner} />
) : (
<button className={styles.generate_btn}>{button}</button>
)}
</div>
)
}
......
......@@ -17,3 +17,10 @@
font-size: 1.5em;
text-align: center;
}
.spinner {
display: flex;
margin: auto;
height: 3em;
width: 3em;
}
import React from 'react'
import styles from './BlogArticle.module.css'
const BlogArticle = ({ article }) => {
const createMarkup = () => {
return { __html: article.description }
}
return (
<div className={styles.blog_article}>
<span className={styles.title}>{article.title}</span>
<div
className={styles.description}
dangerouslySetInnerHTML={createMarkup()}
></div>
</div>
)
}
export default BlogArticle
.blog_article {
margin: 1em 0;
overflow: hidden;
}
.title {
display: block;
font-size: 2em;
text-align: center;
margin: 10px 0;
}
.description {
width: 100%;
/* blockquote {
--reach-tabs: 1;
--reach-menu-button: 1;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
color: white;
font-family: medium-content-sans-serif-font, -apple-system,
BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
'Open Sans', 'Helvetica Neue', sans-serif;
font-weight: 400;
word-break: break-word;
word-wrap: break-word;
box-sizing: inherit;
margin: 0;
box-shadow: inset 3px 0 0 0 rgba(41, 41, 41, 1);
padding-left: 23px;
margin-left: -20px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
word-wrap: break-word;
box-sizing: inherit;
margin: 0;
font-weight: 400;
word-break: break-word;
font-style: italic;
font-family: charter, Georgia, Cambria, 'Times New Roman', Times, serif;
margin-bottom: -0.46em;
font-size: 21px;
margin-top: 2em;
line-height: 32px;
letter-spacing: -0.003em;
strong {
--reach-tabs: 1;
--reach-menu-button: 1;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
word-wrap: break-word;
word-break: break-word;
color: white;
font-style: italic;
font-size: 21px;
line-height: 32px;
letter-spacing: -0.003em;
box-sizing: inherit;
font-family: charter, Georgia, Cambria, 'Times New Roman', Times, serif;
font-weight: 700;
}
} */
* {
width: 100%;
overflow: hidden;
}
img {
width: 100%;
}
figure {
img {
width: 100%;
}
figcaption {
font-size: 0.8em;
text-align: center;
color: var(--black-200);
}
}
p {
margin: 20px 0;
font-size: 1.2em;
}
pre {
padding: 20px;
background-color: var(black);
font-size: 1.1em;
font-family: 'Roboto Mono', monospace !important;
}
pre::first-line {
font-weight: bold;
}
ul {
margin-left: 10px;
padding: 0;
list-style: none;
list-style-image: none;
font-size: 1.1em;
li {
letter-spacing: -0.003em;
line-height: 32px;
margin-left: 30px;
/* margin-top: 2em; */
list-style: inside;
list-style-type: disc !important;
margin-bottom: -0.46em;
}
}
h4 {
font-size: 1.3em;
margin: 10px 0;
}
h3 {
font-size: 1.4em !important;
margin: 20px 0 10px 0;
}
a {
color: var(--blue-300);
}
a:hover {
color: var(--blue-600);
}
}
import React, { useState } from 'react'
import AutomatedAnswerSwiper from '../automated-answer-swiper/AutomatedAnswerSwiper'
import BlogArticle from '../blog-article/BlogArticle'
import ShowHide from '../show-hide/ShowHide'
import styles from './BlogsWrapper.module.css'
const BlogsWrapper = ({ source, articles, resources }) => {
const [show, setShow] = useState(false)
return (
<>
{articles && articles.length > 0 ? (
<>
<h1 className={styles.h1}>
Here {articles.length > 1 ? 'are' : 'is'} {articles.length} article
{articles.length > 1 && 's'} I found on {source}
<ShowHide show={show} onClick={() => setShow(!show)} />
</h1>
{show && (
<div className={styles.blogs_wrapper}>
<AutomatedAnswerSwiper>
{articles.map((article, i) => {
return <BlogArticle key={i} article={article} />
})}
</AutomatedAnswerSwiper>
</div>
)}
</>
) : (
<h1>
No {source} articles found for this question. Please refer the extra
resouces to find more info
</h1>
)}
</>
)
}
export default BlogsWrapper
.blogs_wrapper {
margin-top: 0.5em;
background-color: #2d2d2d;
padding: 0.8em;
border-radius: 2px;
margin-bottom: 3em;
width: 100%;
overflow: hidden;
}
.h1 {
font-size: 2em !important;
display: flex;
justify-content: space-between;
align-items: center;
}
import React from 'react'
const EyeIcon = ({ width, height, color }) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
id="Capa_1"
width={width}
height={height}
fill={color}
viewBox="0 0 511.992 511.992"
enableBackground="new 0 0 512 512"
>
<g>
<g>
<path d="M510.096,249.937c-4.032-5.867-100.928-143.275-254.101-143.275C124.56,106.662,7.44,243.281,2.512,249.105 c-3.349,3.968-3.349,9.792,0,13.781C7.44,268.71,124.56,405.329,255.995,405.329S504.549,268.71,509.477,262.886 C512.571,259.217,512.848,253.905,510.096,249.937z M255.995,383.996c-105.365,0-205.547-100.48-230.997-128 c25.408-27.541,125.483-128,230.997-128c123.285,0,210.304,100.331,231.552,127.424 C463.013,282.065,362.256,383.996,255.995,383.996z" />
</g>
</g>
<g>
<g>
<path d="M255.995,170.662c-47.061,0-85.333,38.272-85.333,85.333s38.272,85.333,85.333,85.333s85.333-38.272,85.333-85.333 S303.056,170.662,255.995,170.662z M255.995,319.996c-35.285,0-64-28.715-64-64s28.715-64,64-64s64,28.715,64,64 S291.28,319.996,255.995,319.996z" />
</g>
</g>
</svg>
)
}
export default EyeIcon
import React from 'react'
const EyeCloseIcon = ({ width, height, color }) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
id="Capa_1"
width={width}
height={height}
fill={color}
viewBox="0 0 511.992 511.992"
enableBackground="new 0 0 512 512"
>
<g>
<g>
<path d="M316.332,195.662c-4.16-4.16-10.923-4.16-15.083,0c-4.16,4.16-4.16,10.944,0,15.083 c12.075,12.075,18.752,28.139,18.752,45.248c0,35.285-28.715,64-64,64c-17.109,0-33.173-6.656-45.248-18.752 c-4.16-4.16-10.923-4.16-15.083,0c-4.16,4.139-4.16,10.923,0,15.083c16.085,16.128,37.525,25.003,60.331,25.003 c47.061,0,85.333-38.272,85.333-85.333C341.334,233.187,332.46,211.747,316.332,195.662z" />
</g>
</g>
<g>
<g>
<path d="M270.87,172.131c-4.843-0.853-9.792-1.472-14.869-1.472c-47.061,0-85.333,38.272-85.333,85.333 c0,5.077,0.619,10.027,1.493,14.869c0.917,5.163,5.419,8.811,10.475,8.811c0.619,0,1.237-0.043,1.877-0.171 c5.781-1.024,9.664-6.571,8.64-12.352c-0.661-3.627-1.152-7.317-1.152-11.157c0-35.285,28.715-64,64-64 c3.84,0,7.531,0.491,11.157,1.131c5.675,1.152,11.328-2.859,12.352-8.64C280.534,178.702,276.652,173.155,270.87,172.131z" />
</g>
</g>
<g>
<g>
<path d="M509.462,249.102c-2.411-2.859-60.117-70.208-139.712-111.445c-5.163-2.709-11.669-0.661-14.379,4.587 c-2.709,5.227-0.661,11.669,4.587,14.379c61.312,31.744,110.293,81.28,127.04,99.371c-25.429,27.541-125.504,128-230.997,128 c-35.797,0-71.872-8.64-107.264-25.707c-5.248-2.581-11.669-0.341-14.229,4.971c-2.581,5.291-0.341,11.669,4.971,14.229 c38.293,18.496,77.504,27.84,116.523,27.84c131.435,0,248.555-136.619,253.483-142.443 C512.854,258.915,512.833,253.091,509.462,249.102z" />
</g>
</g>
<g>
<g>
<path d="M325.996,118.947c-24.277-8.171-47.829-12.288-69.995-12.288c-131.435,0-248.555,136.619-253.483,142.443 c-3.115,3.669-3.371,9.003-0.597,12.992c1.472,2.112,36.736,52.181,97.856,92.779c1.813,1.216,3.84,1.792,5.888,1.792 c3.435,0,6.827-1.664,8.875-4.8c3.264-4.885,1.92-11.52-2.987-14.763c-44.885-29.845-75.605-65.877-87.104-80.533 c24.555-26.667,125.291-128.576,231.552-128.576c19.861,0,41.131,3.755,63.189,11.157c5.589,2.005,11.648-1.088,13.504-6.699 C334.572,126.862,331.585,120.825,325.996,118.947z" />
</g>
</g>
<g>
<g>
<path d="M444.865,67.128c-4.16-4.16-10.923-4.16-15.083,0L67.116,429.795c-4.16,4.16-4.16,10.923,0,15.083 c2.091,2.069,4.821,3.115,7.552,3.115c2.731,0,5.461-1.045,7.531-3.115L444.865,82.211 C449.025,78.051,449.025,71.288,444.865,67.128z" />
</g>
</g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
<g></g>
</svg>
)
}
export default EyeCloseIcon
......@@ -11,7 +11,7 @@ const Pagination = ({
return (
<div className={styles.pagination_wrapper}>
<span onClick={onClickFirst} className={styles.cursor}>
first
&lt;&lt;
</span>
<div className={styles.numbering}>
{new Array(length).fill(0).map((item, index) => {
......@@ -33,7 +33,7 @@ const Pagination = ({
<span className={styles.muted_number}>{length}</span> */}
</div>
<span onClick={onClickLast} className={styles.cursor}>
last
&gt;&gt;
</span>
</div>
)
......
.pagination_wrapper {
display: flex;
flex-direction: row;
justify-content: space-between;
justify-content: center;
align-items: center;
}
.numbering {
......@@ -27,5 +28,10 @@
}
.cursor {
margin: 0 10px;
cursor: pointer;
color: #fff;
padding: 4px 10px;
background-color: #2d2d2d;
border: 1px solid #78ff78;
}
......@@ -10,7 +10,6 @@ import Textarea from '../../textarea'
import FormInput from '../../form-input'
import TagInput from '../../tag-input'
import SimilarQuestionSuggestions from '../../similar-question-suggestions'
import styles from './question-form.module.css'
const QuestionForm = () => {
......
import React from 'react'
import EyeIcon from '../icons/Eye'
import EyeCloseIcon from '../icons/EyeClose'
import styles from './ShowHide.module.css'
const ShowHide = ({ onClick, show }) => {
return (
<div onClick={onClick} className={styles.show_hide}>
{show ? (
<span>
<EyeCloseIcon width={24} height={24} color="red" /> Hide
</span>
) : (
<span>
<EyeIcon width={24} height={24} color="white" /> Show
</span>
)}
</div>
)
}
export default ShowHide
.show_hide {
cursor: pointer;
font-size: 1.5rem;
background-color: var(--black-800);
padding: 2px 10px;
border-radius: 5px;
span {
display: flex;
align-items: center;
* {
margin-right: 5px;
}
}
}
......@@ -29,6 +29,5 @@
--powder-700: #39739d;
--powder-800: #2c5777;
--main-purple: #800080;
--fade: 120ms;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment