diff --git a/server/src/app/api/v1/__init__.py b/server/src/app/api/v1/__init__.py
index 37b1ddf..9713036 100644
--- a/server/src/app/api/v1/__init__.py
+++ b/server/src/app/api/v1/__init__.py
@@ -3,8 +3,10 @@ from fastapi import APIRouter
from .knowledges import router as knowledge_router
from .metrics import router as metric_router
from .auth import router as auth_router
+from .questions import router as question_router
router = APIRouter(prefix="/v1")
router.include_router(knowledge_router)
+router.include_router(question_router)
router.include_router(metric_router)
router.include_router(auth_router)
\ No newline at end of file
diff --git a/server/src/app/api/v1/__pycache__/__init__.cpython-311.pyc b/server/src/app/api/v1/__pycache__/__init__.cpython-311.pyc
index e682e78..51b78f3 100644
Binary files a/server/src/app/api/v1/__pycache__/__init__.cpython-311.pyc and b/server/src/app/api/v1/__pycache__/__init__.cpython-311.pyc differ
diff --git a/server/src/app/api/v1/__pycache__/knowledges.cpython-311.pyc b/server/src/app/api/v1/__pycache__/knowledges.cpython-311.pyc
index 5d042c4..c6e6378 100644
Binary files a/server/src/app/api/v1/__pycache__/knowledges.cpython-311.pyc and b/server/src/app/api/v1/__pycache__/knowledges.cpython-311.pyc differ
diff --git a/server/src/app/api/v1/__pycache__/metrics.cpython-311.pyc b/server/src/app/api/v1/__pycache__/metrics.cpython-311.pyc
index 06ec38f..702e857 100644
Binary files a/server/src/app/api/v1/__pycache__/metrics.cpython-311.pyc and b/server/src/app/api/v1/__pycache__/metrics.cpython-311.pyc differ
diff --git a/server/src/app/api/v1/__pycache__/questions.cpython-311.pyc b/server/src/app/api/v1/__pycache__/questions.cpython-311.pyc
new file mode 100644
index 0000000..2c3d0f4
Binary files /dev/null and b/server/src/app/api/v1/__pycache__/questions.cpython-311.pyc differ
diff --git a/server/src/app/api/v1/knowledges.py b/server/src/app/api/v1/knowledges.py
index 3ba5824..4649c69 100644
--- a/server/src/app/api/v1/knowledges.py
+++ b/server/src/app/api/v1/knowledges.py
@@ -44,7 +44,7 @@ def generate_questions(id: int, current_user: Annotated[str, Depends(get_current
)
questions_raw = questions_generation(knowledge)
for q in questions_raw:
- question = Question(question = q, knowledge=knowledge)
+ question = Question(question = q, knowledge=knowledge, user=current_user)
create_question(question)
return questions_raw
diff --git a/server/src/app/api/v1/metrics.py b/server/src/app/api/v1/metrics.py
index de06eee..7a4ff9f 100644
--- a/server/src/app/api/v1/metrics.py
+++ b/server/src/app/api/v1/metrics.py
@@ -8,8 +8,8 @@ from src.app.auth.dependancies import get_current_user
router = APIRouter(tags=["metrics"])
-@router.post("/metrics/")
-def create(metric_data: MetricCreate, current_user: Annotated[str, Depends(get_current_user)]):
- metric: Metric = Metric(question_id = metric_data.question_id, need_index = metric_data.need_index, user = current_user)
- created_metric: Metric = create_metric(metric)
- return created_metric
+# @router.post("/metrics/")
+# def create(metric_data: MetricCreate, current_user: Annotated[str, Depends(get_current_user)]):
+# metric: Metric = Metric(question_id = metric_data.question_id, need_index = metric_data.need_index, user = current_user)
+# created_metric: Metric = create_metric(metric)
+# return created_metric
diff --git a/server/src/app/api/v1/questions.py b/server/src/app/api/v1/questions.py
new file mode 100644
index 0000000..a420f58
--- /dev/null
+++ b/server/src/app/api/v1/questions.py
@@ -0,0 +1,38 @@
+from typing import Annotated
+from fastapi import APIRouter, Depends, HTTPException, status
+
+from src.app.auth.dependancies import get_current_user
+
+from src.app.models.question import Question
+from src.app.models.metric import Metric, MetricCreate
+
+from src.app.data.question import get_question_by_id
+from src.app.data.metric import get_metrics, create_metric
+
+#Added in __ini__
+router = APIRouter(tags=["questions"])
+
+@router.get("/questions/{id}/metrics")
+def read_questions(id: int, current_user: Annotated[str, Depends(get_current_user)]):
+ question: Question = get_question_by_id(id, current_user)
+ if not question:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Forbidden. The requested knowledge is not available for the provided ID.",
+ headers={"WWW-Authenticate": "Bearer"},
+ )
+ metrics = get_metrics(question)
+ return metrics
+
+@router.post("/questions/{id}/metrics")
+def create(id: int, metric_data: MetricCreate, current_user: Annotated[str, Depends(get_current_user)]):
+ question: Question = get_question_by_id(id, current_user)
+ if not question:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Forbidden. The requested knowledge is not available for the provided ID.",
+ headers={"WWW-Authenticate": "Bearer"},
+ )
+ metric: Metric = Metric(question_id = id, need_index = metric_data.need_index, user = current_user)
+ created_metric: Metric = create_metric(metric)
+ return created_metric
\ No newline at end of file
diff --git a/server/src/app/data/__pycache__/question.cpython-311.pyc b/server/src/app/data/__pycache__/question.cpython-311.pyc
index d424742..909e1be 100644
Binary files a/server/src/app/data/__pycache__/question.cpython-311.pyc and b/server/src/app/data/__pycache__/question.cpython-311.pyc differ
diff --git a/server/src/app/data/question.py b/server/src/app/data/question.py
index 382d12e..29a3d6c 100644
--- a/server/src/app/data/question.py
+++ b/server/src/app/data/question.py
@@ -1,13 +1,21 @@
from sqlmodel import Session, select
from src.app.models.question import Question
+from src.app.models.user import User
from src.app.database import engine
-def get_question_by_id(question_id: int):
+def get_question_by_id(question_id: int, user: User):
with Session(engine) as session:
- question = session.get(Question, question_id)
+ statement = select(Question).where(Question.id == question_id, Question.user_id == user.id)
+ results = session.exec(statement)
+ question = results.first()
return question
+# def get_question_by_id(question_id: int):
+# with Session(engine) as session:
+# question = session.get(Question, question_id)
+# return question
+
def get_questions(knowledge):
with Session(engine) as session:
statement = select(Question).where(Question.knowledge_id == knowledge.id)
diff --git a/server/src/app/models/__pycache__/metric.cpython-311.pyc b/server/src/app/models/__pycache__/metric.cpython-311.pyc
index 5ec8f65..455cd1f 100644
Binary files a/server/src/app/models/__pycache__/metric.cpython-311.pyc and b/server/src/app/models/__pycache__/metric.cpython-311.pyc differ
diff --git a/server/src/app/models/metric.py b/server/src/app/models/metric.py
index 42ba569..54289a6 100644
--- a/server/src/app/models/metric.py
+++ b/server/src/app/models/metric.py
@@ -15,5 +15,4 @@ class Metric(SQLModel, table=True):
user: User | None = Relationship(back_populates="metrics")
class MetricCreate(BaseModel):
- question_id: int
need_index: int
\ No newline at end of file
diff --git a/user-interface/src/App.vue b/user-interface/src/App.vue
index f9f122b..8a3f7fc 100644
--- a/user-interface/src/App.vue
+++ b/user-interface/src/App.vue
@@ -21,14 +21,4 @@ import AppTopbar from '@/components/AppTopbar.vue'
padding-inline: 5%;
height: 100vh;
}
-
-/* main {
- height: 100%;
-}
-
-@media screen and (min-width: 768px) {
- #app {
- height: 100vh;
- }
-} */
diff --git a/user-interface/src/components/CollectKnowledge.vue b/user-interface/src/components/CollectKnowledge.vue
index 9ff87f6..a1c3976 100644
--- a/user-interface/src/components/CollectKnowledge.vue
+++ b/user-interface/src/components/CollectKnowledge.vue
@@ -1,11 +1,10 @@
@@ -80,7 +91,7 @@
diff --git a/user-interface/src/components/GenerateQuestion.vue b/user-interface/src/components/GenerateQuestion.vue
index ca74258..58dddd8 100644
--- a/user-interface/src/components/GenerateQuestion.vue
+++ b/user-interface/src/components/GenerateQuestion.vue
@@ -3,10 +3,12 @@
import { BProgress } from "buefy";
import type { Knowledge } from "@/types/types";
- import api from "@/services/apiAxios";
+ import api from "@/services/api";
import { useItemStore } from '@/stores/item'
import { useStepStore } from '@/stores/step'
+ import { ProcessStep } from '@/services/knowledgeWorklow'
+
const stepStore = useStepStore()
const itemStore = useItemStore()
@@ -18,7 +20,8 @@
async function generateQuestions (knowledge: Knowledge) {
await api.post(`api/v1/knowledges/${knowledge.id}/questions`)
- stepStore.nextStep()
+ if(stepStore.indexStep == ProcessStep.WaitGeneration)
+ stepStore.nextStep()
}
diff --git a/user-interface/src/components/ReadKnowledges.vue b/user-interface/src/components/ReadKnowledges.vue
index 99bc9ee..e9d1088 100644
--- a/user-interface/src/components/ReadKnowledges.vue
+++ b/user-interface/src/components/ReadKnowledges.vue
@@ -1,17 +1,148 @@
-
-
Knowledge
-
+
\ No newline at end of file
diff --git a/user-interface/src/router/index.ts b/user-interface/src/router/index.ts
index 852944f..0b88ba3 100644
--- a/user-interface/src/router/index.ts
+++ b/user-interface/src/router/index.ts
@@ -1,5 +1,5 @@
import { createRouter, createWebHistory } from 'vue-router'
-import { isAuthenticated } from '@/services/apiAxios'
+import { isAuthenticated } from '@/services/api'
const pagesWithoutGuard = ['login', 'app', 'register']
@@ -33,9 +33,10 @@ const router = createRouter({
],
})
+// Guard system
router.beforeEach(async (to, from) => {
const isAuth = await isAuthenticated()
- if (!isAuth && pagesWithoutGuard.includes(to.name!.toString())) {
+ if (!isAuth && !pagesWithoutGuard.includes(to.name!.toString())) {
return { name: 'login' }
}
})
diff --git a/user-interface/src/services/api.ts b/user-interface/src/services/api.ts
index f6045f2..4250a94 100644
--- a/user-interface/src/services/api.ts
+++ b/user-interface/src/services/api.ts
@@ -1,74 +1,45 @@
-class ApiClient {
- private baseURL: string
- private defaultHeaders: Record
+import axios from "axios"
+import type { AxiosResponse } from "axios";
- constructor(baseURL: string) {
- this.baseURL = baseURL
- this.defaultHeaders = {
- 'Content-Type': 'application/json',
+const api = axios.create({
+ baseURL: import.meta.env.VITE_API_URL
+});
+
+api.interceptors.request.use((config) => {
+ const token = localStorage.getItem('access_token')
+ if (token){
+ config.headers.Authorization = `Bearer ${token}`
}
- }
+ return config
+})
- private async request(endpoint: string, options: RequestInit = {}): Promise {
- const url = `${this.baseURL}${endpoint}`
- const config: RequestInit = {
- headers: { ...this.defaultHeaders, ...options.headers },
- ...options,
- }
-
- let response
+export const authAPI = {
+ register: (username: string, password: string) =>
+ api.post(
+ '/api/v1/auth/register',
+ { "username":username, "plain_password":password }
+ ),
+ login: (username: string, password: string) =>
+ api.post(
+ '/api/v1/auth/login',
+ new URLSearchParams({ username, password }),
+ { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }}
+ ),
+ getMe: () => api.get('/api/v1/auth/me')
+}
+export const isAuthenticated = async () => {
try {
- response = await fetch(url, config)
- } catch (error) {
- if (error instanceof Error) {
- throw new HTTPError(`Network error: ${error.message}`)
- }
- throw new HTTPError('Unknown network error')
+ const response: AxiosResponse = await authAPI.getMe()
+ if (response.status==200)
+ return true
+ else
+ return false
}
-
- if (response?.ok) {
- return response.json()
+ catch{
+ return false
}
-
- const errorData = await response.json().catch(() => ({}))
- throw new HTTPError(
- errorData.message || `HTTP ${response.status}: ${response.statusText}`,
- response.status,
- response,
- )
- }
-
- /**
- * HTTP Get
- * @param endpoint
- * @returns
- */
-
- async get(endpoint: string): Promise {
- return this.request(endpoint, { method: 'GET' })
- }
-
- /**
- * HTTP Post
- * @param endpoint
- * @param data
- * @returns
- */
- async post(endpoint: string, data: unknown): Promise {
- return this.request(endpoint, { method: 'POST', body: JSON.stringify(data) })
- }
+
}
-class HTTPError extends Error {
- constructor(
- message: string,
- private status: number | null = null,
- private reponse: Response | null = null,
- ) {
- super(message)
- this.name = 'HTTPError'
- }
-}
-
-export const apiClient = new ApiClient(import.meta.env.VITE_API_URL)
\ No newline at end of file
+export default api;
\ No newline at end of file
diff --git a/user-interface/src/services/apiAxios.ts b/user-interface/src/services/apiAxios.ts
deleted file mode 100644
index 828d996..0000000
--- a/user-interface/src/services/apiAxios.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import axios from "axios"
-import type { AxiosResponse } from "axios";
-
-const api = axios.create({
- baseURL: import.meta.env.VITE_API_URL
-});
-
-api.interceptors.request.use((config) => {
- const token = localStorage.getItem('access_token')
- if (token){
- config.headers.Authorization = `Bearer ${token}`
- }
- return config
-})
-
-export const authAPI = {
- register: (username: string, password: string) =>
- api.post(
- '/api/v1/auth/register',
- { "username":username, "plain_password":password }
- ),
- login: (username: string, password: string) =>
- api.post(
- '/api/v1/auth/login',
- new URLSearchParams({ username, password }),
- { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }}
- ),
- getMe: () => api.get('/api/v1/auth/me')
-}
-
-export const isAuthenticated = async () => {
- try {
- const response: AxiosResponse = await authAPI.getMe()
- if (response.status==200)
- return true
- else
- return false
- }
- catch{
- return false
- }
-
-}
-
-export default api;
\ No newline at end of file
diff --git a/user-interface/src/services/knowledgeWorklow.ts b/user-interface/src/services/knowledgeWorklow.ts
new file mode 100644
index 0000000..28aecb1
--- /dev/null
+++ b/user-interface/src/services/knowledgeWorklow.ts
@@ -0,0 +1,54 @@
+import type { Knowledge, Metric, Question } from "@/types/types"
+import api from "@/services/api"
+import type { AxiosResponse } from "axios"
+
+const processFlow = ["CollectKnowledge", "GenerateQuestion", "EvaluateQuestion", "Done"]
+
+//From user action in worlfow
+export enum ProcessStep {
+ CollectKnowledge,
+ WaitGeneration,
+ EvaluateQuestion,
+ ProcessDone
+}
+
+export async function identifyProcessStep(knowledge: Knowledge | null): Promise{
+ if (knowledge == null)
+ return ProcessStep.CollectKnowledge
+
+ //Check questions exist
+ const apiGetQuestions: AxiosResponse = await api.get(
+ `api/v1/knowledges/${knowledge.id}/questions/`,
+ {
+ validateStatus: function (status) {
+ return status < 500
+ },
+ })
+ if (apiGetQuestions.status >= 400 && apiGetQuestions.status < 500)
+ return ProcessStep.WaitGeneration
+
+ const questions: Question[] = apiGetQuestions.data
+ if (questions.length == 0)
+ return ProcessStep.WaitGeneration
+
+ //Check metrics exist
+ const apiGetMetrics: AxiosResponse = await api.get(
+ `api/v1/questions/${questions[0]!.id}/metrics/`,
+ {
+ validateStatus: function (status) {
+ return status < 500
+ },
+ })
+ if (apiGetMetrics.status >= 400 && apiGetMetrics.status < 500)
+ return ProcessStep.EvaluateQuestion
+
+ const metrics: Metric[] = apiGetMetrics.data
+ if (metrics.length == 0)
+ return ProcessStep.EvaluateQuestion
+
+ //Data is complete
+ return ProcessStep.ProcessDone
+ }
+
+
+
\ No newline at end of file
diff --git a/user-interface/src/stores/step.ts b/user-interface/src/stores/step.ts
index 503f363..e5aa3d4 100644
--- a/user-interface/src/stores/step.ts
+++ b/user-interface/src/stores/step.ts
@@ -5,6 +5,8 @@ import CollectKnowledge from '@/components/CollectKnowledge.vue'
import EvaluateQuestion from '@/components/EvaluateQuestion.vue'
import GenerateQuestion from '@/components/GenerateQuestion.vue'
+import { ProcessStep } from '@/services/knowledgeWorklow'
+
const steps: Component = [
CollectKnowledge,
GenerateQuestion,
@@ -17,8 +19,15 @@ export const useStepStore = defineStore('step', () => {
const getCurrentComponent = computed(() => steps[indexStep.value])
function nextStep() {
- indexStep.value++
+ if(indexStep.value + 1 < steps.length)
+ indexStep.value++
+ else
+ indexStep.value = 0
}
- return { steps, getCurrentComponent, nextStep }
+ function goToStep(processStep: ProcessStep){
+ indexStep.value = processStep
+ }
+
+ return { indexStep, steps, getCurrentComponent, nextStep, goToStep }
})
diff --git a/user-interface/src/types/types.d.ts b/user-interface/src/types/types.d.ts
index b0e2d65..dbdd002 100644
--- a/user-interface/src/types/types.d.ts
+++ b/user-interface/src/types/types.d.ts
@@ -18,9 +18,15 @@ interface Question {
user: User
}
-interface MetricCreate {
+interface Metric {
+ //id
question_id: number,
need_index: number
+ //user
+}
+
+interface MetricCreate {
+ need_index: number
}
interface User {
@@ -28,4 +34,4 @@ interface User {
token: string
}
-export type {KnowledgeCreate, Knowledge, Question, MetricCreate}
\ No newline at end of file
+export type {KnowledgeCreate, Knowledge, Question, MetricCreate, Metric}
\ No newline at end of file
diff --git a/user-interface/src/views/LoginView.vue b/user-interface/src/views/LoginView.vue
index fa380e4..8eeb529 100644
--- a/user-interface/src/views/LoginView.vue
+++ b/user-interface/src/views/LoginView.vue
@@ -2,7 +2,7 @@
import router from '@/router/index'
import { BField, BInput, BButton, useToast } from "buefy";
import { ref } from "vue"
- import { authAPI } from '@/services/apiAxios'
+ import { authAPI } from '@/services/api'
const username = ref("")
const password = ref("")
diff --git a/user-interface/src/views/RegisterView.vue b/user-interface/src/views/RegisterView.vue
index 57c593f..1b1d68e 100644
--- a/user-interface/src/views/RegisterView.vue
+++ b/user-interface/src/views/RegisterView.vue
@@ -2,7 +2,7 @@
import router from '@/router/index'
import { BField, BInput, BButton, useToast } from "buefy";
import { ref } from "vue"
- import { authAPI } from '@/services/apiAxios'
+ import { authAPI } from '@/services/api'
const username = ref("")
const password = ref("")