Reorganizacion de proyecto
This commit is contained in:
parent
0d78fb9ccb
commit
93485eb700
Binary file not shown.
|
|
@ -5,8 +5,8 @@ from sqlalchemy import pool
|
||||||
|
|
||||||
from alembic import context
|
from alembic import context
|
||||||
|
|
||||||
from database import Base
|
from app.db.database import Base
|
||||||
from models import *
|
from app.models import *
|
||||||
import os
|
import os
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,6 +1,6 @@
|
||||||
from database import engine, Base, SessionLocal
|
from app.db.database import engine, Base, SessionLocal
|
||||||
from models import User, Project, Specialty, Contractor, Activity, NonConformity, Evidence, UserRole
|
from app.models import User, Project, Specialty, Contractor, Activity, NonConformity, Evidence, UserRole
|
||||||
from security import get_password_hash # Import hashing function
|
from app.security import get_password_hash # Import hashing function
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
def init_db():
|
def init_db():
|
||||||
Binary file not shown.
|
|
@ -1,6 +1,6 @@
|
||||||
from sqlalchemy import Table, Column, Integer, String, Boolean, ForeignKey, DateTime, Text, Enum, JSON
|
from sqlalchemy import Table, Column, Integer, String, Boolean, ForeignKey, DateTime, Text, Enum, JSON
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
from database import Base
|
from app.db.database import Base
|
||||||
import datetime
|
import datetime
|
||||||
import enum
|
import enum
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -3,29 +3,35 @@ import shutil
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File, BackgroundTasks
|
from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File, BackgroundTasks
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from database import get_db
|
|
||||||
from models import Activity, Evidence, User
|
|
||||||
from security import get_current_active_user
|
|
||||||
import schemas
|
|
||||||
import uuid
|
import uuid
|
||||||
from services import activities
|
from app.db.database import get_db
|
||||||
|
from app.models.models import Activity, Evidence, User
|
||||||
|
from app.security import get_current_active_user
|
||||||
|
from app.services.activities import ActivityService
|
||||||
|
from app import schemas
|
||||||
|
from app.schemas import ActivityCreate, ActivityUpdate
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/activities",
|
prefix="/activities",
|
||||||
tags=["Activities"]
|
tags=["Activities"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_activity_service(
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
current_user: User = Depends(get_current_active_user)
|
||||||
|
) -> ActivityService:
|
||||||
|
return ActivityService(db, current_user)
|
||||||
|
|
||||||
UPLOAD_DIR = "uploads"
|
UPLOAD_DIR = "uploads"
|
||||||
if not os.path.exists(UPLOAD_DIR):
|
if not os.path.exists(UPLOAD_DIR):
|
||||||
os.makedirs(UPLOAD_DIR)
|
os.makedirs(UPLOAD_DIR)
|
||||||
|
|
||||||
@router.post("/", response_model=schemas.Activity)
|
@router.post("/", response_model=schemas.Activity)
|
||||||
def create_activity(
|
def create_activity(
|
||||||
activity: schemas.ActivityCreate,
|
activity: ActivityCreate,
|
||||||
db: Session = Depends(get_db),
|
service: ActivityService = Depends(get_activity_service)
|
||||||
current_user: User = Depends(get_current_active_user)
|
|
||||||
):
|
):
|
||||||
return activities.create_activity(db, activity, current_user)
|
return service.create_activity(activity)
|
||||||
|
|
||||||
@router.get("/", response_model=List[schemas.Activity])
|
@router.get("/", response_model=List[schemas.Activity])
|
||||||
def read_activities(
|
def read_activities(
|
||||||
|
|
@ -33,36 +39,25 @@ def read_activities(
|
||||||
specialty_id: Optional[int] = None,
|
specialty_id: Optional[int] = None,
|
||||||
skip: int = 0,
|
skip: int = 0,
|
||||||
limit: int = 100,
|
limit: int = 100,
|
||||||
db: Session = Depends(get_db),
|
service: ActivityService = Depends(get_activity_service)
|
||||||
current_user: User = Depends(get_current_active_user)
|
|
||||||
):
|
):
|
||||||
db_activities = activities.get_activities(db, current_user, project_id, specialty_id, skip, limit)
|
db_activities = service.get_activities(project_id, specialty_id, skip, limit)
|
||||||
return db_activities
|
return db_activities
|
||||||
|
|
||||||
@router.get("/{activity_id}", response_model=schemas.Activity)
|
@router.get("/{activity_id}", response_model=schemas.Activity)
|
||||||
def read_activity(
|
def read_activity(
|
||||||
activity_id: int,
|
activity_id: int,
|
||||||
db: Session = Depends(get_db)
|
service: ActivityService = Depends(get_activity_service)
|
||||||
):
|
):
|
||||||
return activities.get_activity(db, activity_id)
|
return service.get_activity(activity_id)
|
||||||
|
|
||||||
@router.put("/{activity_id}", response_model=schemas.Activity)
|
@router.put("/{activity_id}", response_model=schemas.Activity)
|
||||||
def update_activity(
|
def update_activity(
|
||||||
activity_id: int,
|
activity_id: int,
|
||||||
activity: schemas.ActivityUpdate,
|
activity: schemas.ActivityUpdate,
|
||||||
db: Session = Depends(get_db),
|
service: ActivityService = Depends(get_activity_service)
|
||||||
current_user: User = Depends(get_current_active_user)
|
|
||||||
):
|
):
|
||||||
db_activity = db.query(Activity).filter(Activity.id == activity_id).first()
|
db_activity = service.update_activity(activity_id, activity)
|
||||||
if not db_activity:
|
|
||||||
raise HTTPException(status_code=404, detail="Activity not found")
|
|
||||||
|
|
||||||
update_data = activity.dict(exclude_unset=True)
|
|
||||||
for key, value in update_data.items():
|
|
||||||
setattr(db_activity, key, value)
|
|
||||||
|
|
||||||
db.commit()
|
|
||||||
db.refresh(db_activity)
|
|
||||||
return db_activity
|
return db_activity
|
||||||
|
|
||||||
@router.post("/{activity_id}/upload", response_model=schemas.Evidence)
|
@router.post("/{activity_id}/upload", response_model=schemas.Evidence)
|
||||||
|
|
@ -72,53 +67,16 @@ async def upload_evidence(
|
||||||
file: UploadFile = File(...),
|
file: UploadFile = File(...),
|
||||||
description: Optional[str] = None,
|
description: Optional[str] = None,
|
||||||
captured_at: Optional[str] = None,
|
captured_at: Optional[str] = None,
|
||||||
db: Session = Depends(get_db),
|
service: ActivityService = Depends(get_activity_service)
|
||||||
current_user: User = Depends(get_current_active_user)
|
|
||||||
):
|
):
|
||||||
# Verify activity exists
|
|
||||||
db_activity = db.query(Activity).filter(Activity.id == activity_id).first()
|
|
||||||
if not db_activity:
|
|
||||||
raise HTTPException(status_code=404, detail="Activity not found")
|
|
||||||
|
|
||||||
# Generate unique filename
|
db_evidence = service.upload_evidence(
|
||||||
file_ext = os.path.splitext(file.filename)[1]
|
activity_id,
|
||||||
unique_filename = f"{uuid.uuid4()}{file_ext}"
|
background_tasks,
|
||||||
file_path = os.path.join(UPLOAD_DIR, unique_filename)
|
file,
|
||||||
|
description,
|
||||||
# Save file
|
captured_at
|
||||||
with open(file_path, "wb") as buffer:
|
|
||||||
shutil.copyfileobj(file.file, buffer)
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
db_captured_at = None
|
|
||||||
if captured_at:
|
|
||||||
try:
|
|
||||||
db_captured_at = datetime.datetime.fromisoformat(captured_at.replace('Z', '+00:00'))
|
|
||||||
except:
|
|
||||||
db_captured_at = datetime.datetime.utcnow()
|
|
||||||
|
|
||||||
# Determine transcription status
|
|
||||||
initial_status = "none"
|
|
||||||
if file.content_type and "audio" in file.content_type:
|
|
||||||
initial_status = "pending"
|
|
||||||
|
|
||||||
# Save to database
|
|
||||||
db_evidence = Evidence(
|
|
||||||
activity_id=activity_id,
|
|
||||||
file_path=file_path,
|
|
||||||
media_type=file.content_type,
|
|
||||||
description=description,
|
|
||||||
captured_at=db_captured_at,
|
|
||||||
transcription_status=initial_status
|
|
||||||
)
|
)
|
||||||
db.add(db_evidence)
|
|
||||||
db.commit()
|
|
||||||
db.refresh(db_evidence)
|
|
||||||
|
|
||||||
# If it's audio, queue transcription
|
|
||||||
if initial_status == "pending":
|
|
||||||
from services.transcription_worker import process_transcription
|
|
||||||
background_tasks.add_task(process_transcription, db_evidence.id)
|
|
||||||
|
|
||||||
return db_evidence
|
return db_evidence
|
||||||
|
|
||||||
|
|
@ -143,11 +101,10 @@ async def retry_transcription(
|
||||||
db.refresh(db_evidence)
|
db.refresh(db_evidence)
|
||||||
|
|
||||||
# Queue transcription task
|
# Queue transcription task
|
||||||
from services.transcription_worker import process_transcription
|
from app.services.transcription_worker import process_transcription
|
||||||
background_tasks.add_task(process_transcription, db_evidence.id)
|
background_tasks.add_task(process_transcription, db_evidence.id)
|
||||||
|
|
||||||
return db_evidence
|
return db_evidence
|
||||||
return db_evidence
|
|
||||||
|
|
||||||
@router.put("/evidence/{evidence_id}", response_model=schemas.Evidence)
|
@router.put("/evidence/{evidence_id}", response_model=schemas.Evidence)
|
||||||
def update_evidence(
|
def update_evidence(
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from fastapi.security import OAuth2PasswordRequestForm
|
from fastapi.security import OAuth2PasswordRequestForm
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from database import get_db
|
|
||||||
from models import User
|
|
||||||
from security import verify_password, create_access_token
|
|
||||||
import schemas
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
from app.db.database import get_db
|
||||||
|
from app.models.models import User
|
||||||
|
from app.security import verify_password, create_access_token
|
||||||
|
import app.schemas
|
||||||
|
|
||||||
router = APIRouter(tags=["Authentication"])
|
router = APIRouter(tags=["Authentication"])
|
||||||
|
|
||||||
@router.post("/token", response_model=schemas.Token)
|
@router.post("/token", response_model=app.schemas.Token)
|
||||||
def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
|
def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
|
||||||
user = db.query(User).filter(User.email == form_data.username).first()
|
user = db.query(User).filter(User.email == form_data.username).first()
|
||||||
if not user or not verify_password(form_data.password, user.hashed_password):
|
if not user or not verify_password(form_data.password, user.hashed_password):
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from database import get_db
|
from app.db.database import get_db
|
||||||
from models import Contractor
|
from app.models.models import Contractor
|
||||||
from security import get_current_active_user
|
from app.security import get_current_active_user
|
||||||
import schemas
|
import app.schemas
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/contractors",
|
prefix="/contractors",
|
||||||
|
|
@ -12,15 +12,15 @@ router = APIRouter(
|
||||||
dependencies=[Depends(get_current_active_user)]
|
dependencies=[Depends(get_current_active_user)]
|
||||||
)
|
)
|
||||||
|
|
||||||
@router.post("/", response_model=schemas.Contractor)
|
@router.post("/", response_model=app.schemas.Contractor)
|
||||||
def create_contractor(contractor: schemas.ContractorCreate, db: Session = Depends(get_db)):
|
def create_contractor(contractor: app.schemas.ContractorCreate, db: Session = Depends(get_db)):
|
||||||
db_contractor = Contractor(**contractor.dict())
|
db_contractor = Contractor(**contractor.dict())
|
||||||
db.add(db_contractor)
|
db.add(db_contractor)
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(db_contractor)
|
db.refresh(db_contractor)
|
||||||
return db_contractor
|
return db_contractor
|
||||||
|
|
||||||
@router.get("/", response_model=List[schemas.Contractor])
|
@router.get("/", response_model=List[app.schemas.Contractor])
|
||||||
def read_contractors(
|
def read_contractors(
|
||||||
parent_id: Optional[int] = None,
|
parent_id: Optional[int] = None,
|
||||||
only_parents: bool = False,
|
only_parents: bool = False,
|
||||||
|
|
@ -37,18 +37,18 @@ def read_contractors(
|
||||||
query = query.filter(Contractor.is_active == is_active)
|
query = query.filter(Contractor.is_active == is_active)
|
||||||
return query.all()
|
return query.all()
|
||||||
|
|
||||||
@router.get("/{contractor_id}", response_model=schemas.Contractor)
|
@router.get("/{contractor_id}", response_model=app.schemas.Contractor)
|
||||||
def read_contractor(contractor_id: int, db: Session = Depends(get_db)):
|
def read_contractor(contractor_id: int, db: Session = Depends(get_db)):
|
||||||
db_contractor = db.query(Contractor).filter(Contractor.id == contractor_id).first()
|
db_contractor = db.query(Contractor).filter(Contractor.id == contractor_id).first()
|
||||||
if not db_contractor:
|
if not db_contractor:
|
||||||
raise HTTPException(status_code=404, detail="Contractor not found")
|
raise HTTPException(status_code=404, detail="Contractor not found")
|
||||||
return db_contractor
|
return db_contractor
|
||||||
|
|
||||||
@router.put("/{contractor_id}", response_model=schemas.Contractor)
|
@router.put("/{contractor_id}", response_model=app.schemas.Contractor)
|
||||||
@router.patch("/{contractor_id}", response_model=schemas.Contractor)
|
@router.patch("/{contractor_id}", response_model=app.schemas.Contractor)
|
||||||
def update_contractor(
|
def update_contractor(
|
||||||
contractor_id: int,
|
contractor_id: int,
|
||||||
contractor: schemas.ContractorUpdate,
|
contractor: app.schemas.ContractorUpdate,
|
||||||
db: Session = Depends(get_db)
|
db: Session = Depends(get_db)
|
||||||
):
|
):
|
||||||
db_contractor = db.query(Contractor).filter(Contractor.id == contractor_id).first()
|
db_contractor = db.query(Contractor).filter(Contractor.id == contractor_id).first()
|
||||||
|
|
@ -6,9 +6,9 @@ import datetime
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File
|
from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from database import get_db
|
from app.db.database import get_db
|
||||||
from models import NonConformity, Evidence
|
from app.models.models import NonConformity, Evidence
|
||||||
import schemas
|
import app.schemas
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/guest",
|
prefix="/guest",
|
||||||
|
|
@ -19,7 +19,7 @@ UPLOAD_DIR = "uploads"
|
||||||
if not os.path.exists(UPLOAD_DIR):
|
if not os.path.exists(UPLOAD_DIR):
|
||||||
os.makedirs(UPLOAD_DIR)
|
os.makedirs(UPLOAD_DIR)
|
||||||
|
|
||||||
@router.get("/nc/{access_hash}", response_model=schemas.NonConformity)
|
@router.get("/nc/{access_hash}", response_model=app.schemas.NonConformity)
|
||||||
def read_guest_nc(
|
def read_guest_nc(
|
||||||
access_hash: str,
|
access_hash: str,
|
||||||
db: Session = Depends(get_db)
|
db: Session = Depends(get_db)
|
||||||
|
|
@ -29,10 +29,10 @@ def read_guest_nc(
|
||||||
raise HTTPException(status_code=404, detail="Non-Conformity not found or invalid link")
|
raise HTTPException(status_code=404, detail="Non-Conformity not found or invalid link")
|
||||||
return db_nc
|
return db_nc
|
||||||
|
|
||||||
@router.patch("/nc/{access_hash}", response_model=schemas.NonConformity)
|
@router.patch("/nc/{access_hash}", response_model=app.schemas.NonConformity)
|
||||||
def update_guest_nc(
|
def update_guest_nc(
|
||||||
access_hash: str,
|
access_hash: str,
|
||||||
nc_update: schemas.NonConformityUpdate,
|
nc_update: app.schemas.NonConformityUpdate,
|
||||||
db: Session = Depends(get_db)
|
db: Session = Depends(get_db)
|
||||||
):
|
):
|
||||||
db_nc = db.query(NonConformity).filter(NonConformity.access_hash == access_hash).first()
|
db_nc = db.query(NonConformity).filter(NonConformity.access_hash == access_hash).first()
|
||||||
|
|
@ -62,7 +62,7 @@ def update_guest_nc(
|
||||||
db.refresh(db_nc)
|
db.refresh(db_nc)
|
||||||
return db_nc
|
return db_nc
|
||||||
|
|
||||||
@router.post("/nc/{access_hash}/upload", response_model=schemas.Evidence)
|
@router.post("/nc/{access_hash}/upload", response_model=app.schemas.Evidence)
|
||||||
async def upload_guest_evidence(
|
async def upload_guest_evidence(
|
||||||
access_hash: str,
|
access_hash: str,
|
||||||
file: UploadFile = File(...),
|
file: UploadFile = File(...),
|
||||||
|
|
@ -5,10 +5,10 @@ import datetime
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File
|
from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from database import get_db
|
from app.db.database import get_db
|
||||||
from models import NonConformity, User, Activity, Evidence, Contractor
|
from app.models.models import NonConformity, User, Activity, Evidence, Contractor
|
||||||
from security import get_current_active_user
|
from app.security import get_current_active_user
|
||||||
import schemas
|
import app.schemas
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/non-conformities",
|
prefix="/non-conformities",
|
||||||
|
|
@ -19,9 +19,9 @@ UPLOAD_DIR = "uploads"
|
||||||
if not os.path.exists(UPLOAD_DIR):
|
if not os.path.exists(UPLOAD_DIR):
|
||||||
os.makedirs(UPLOAD_DIR)
|
os.makedirs(UPLOAD_DIR)
|
||||||
|
|
||||||
@router.post("/", response_model=schemas.NonConformity)
|
@router.post("/", response_model=app.schemas.NonConformity)
|
||||||
def create_nc(
|
def create_nc(
|
||||||
nc: schemas.NonConformityCreate,
|
nc: app.schemas.NonConformityCreate,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
current_user: User = Depends(get_current_active_user)
|
current_user: User = Depends(get_current_active_user)
|
||||||
):
|
):
|
||||||
|
|
@ -41,7 +41,7 @@ def create_nc(
|
||||||
db.refresh(db_nc)
|
db.refresh(db_nc)
|
||||||
return db_nc
|
return db_nc
|
||||||
|
|
||||||
@router.get("/", response_model=List[schemas.NonConformity])
|
@router.get("/", response_model=List[app.schemas.NonConformity])
|
||||||
def read_ncs(
|
def read_ncs(
|
||||||
activity_id: Optional[int] = None,
|
activity_id: Optional[int] = None,
|
||||||
status: Optional[str] = None,
|
status: Optional[str] = None,
|
||||||
|
|
@ -56,7 +56,7 @@ def read_ncs(
|
||||||
|
|
||||||
return query.all()
|
return query.all()
|
||||||
|
|
||||||
@router.get("/{nc_id}", response_model=schemas.NonConformity)
|
@router.get("/{nc_id}", response_model=app.schemas.NonConformity)
|
||||||
def read_nc(
|
def read_nc(
|
||||||
nc_id: int,
|
nc_id: int,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
|
|
@ -67,11 +67,11 @@ def read_nc(
|
||||||
raise HTTPException(status_code=404, detail="Non-Conformity not found")
|
raise HTTPException(status_code=404, detail="Non-Conformity not found")
|
||||||
return db_nc
|
return db_nc
|
||||||
|
|
||||||
@router.put("/{nc_id}", response_model=schemas.NonConformity)
|
@router.put("/{nc_id}", response_model=app.schemas.NonConformity)
|
||||||
@router.patch("/{nc_id}", response_model=schemas.NonConformity)
|
@router.patch("/{nc_id}", response_model=app.schemas.NonConformity)
|
||||||
def update_nc(
|
def update_nc(
|
||||||
nc_id: int,
|
nc_id: int,
|
||||||
nc: schemas.NonConformityUpdate,
|
nc: app.schemas.NonConformityUpdate,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
current_user: User = Depends(get_current_active_user)
|
current_user: User = Depends(get_current_active_user)
|
||||||
):
|
):
|
||||||
|
|
@ -95,7 +95,7 @@ def update_nc(
|
||||||
db.refresh(db_nc)
|
db.refresh(db_nc)
|
||||||
return db_nc
|
return db_nc
|
||||||
|
|
||||||
@router.post("/{nc_id}/upload", response_model=schemas.Evidence)
|
@router.post("/{nc_id}/upload", response_model=app.schemas.Evidence)
|
||||||
async def upload_nc_evidence(
|
async def upload_nc_evidence(
|
||||||
nc_id: int,
|
nc_id: int,
|
||||||
file: UploadFile = File(...),
|
file: UploadFile = File(...),
|
||||||
|
|
@ -175,7 +175,7 @@ def notify_responsible(
|
||||||
db.refresh(db_nc)
|
db.refresh(db_nc)
|
||||||
|
|
||||||
# Send email
|
# Send email
|
||||||
from services.email_service import EmailService
|
from app.services.email_service import EmailService
|
||||||
EmailService.send_nc_notification(db_nc.responsible_email, db_nc.access_hash, db_nc.description)
|
EmailService.send_nc_notification(db_nc.responsible_email, db_nc.access_hash, db_nc.description)
|
||||||
|
|
||||||
# Update status to in-checking
|
# Update status to in-checking
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from typing import List
|
from typing import List
|
||||||
from database import get_db
|
from app.db.database import get_db
|
||||||
from models import Project, Specialty, Contractor
|
from app.models.models import Project, Specialty, Contractor
|
||||||
from security import get_current_active_user
|
from app.security import get_current_active_user
|
||||||
import schemas
|
import app.schemas
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/projects",
|
prefix="/projects",
|
||||||
|
|
@ -12,8 +12,8 @@ router = APIRouter(
|
||||||
dependencies=[Depends(get_current_active_user)]
|
dependencies=[Depends(get_current_active_user)]
|
||||||
)
|
)
|
||||||
|
|
||||||
@router.post("/", response_model=schemas.Project)
|
@router.post("/", response_model=app.schemas.Project)
|
||||||
def create_project(project: schemas.ProjectCreate, db: Session = Depends(get_db)):
|
def create_project(project: app.schemas.ProjectCreate, db: Session = Depends(get_db)):
|
||||||
db_project = db.query(Project).filter(Project.code == project.code).first()
|
db_project = db.query(Project).filter(Project.code == project.code).first()
|
||||||
if db_project:
|
if db_project:
|
||||||
raise HTTPException(status_code=400, detail="Project code already exists")
|
raise HTTPException(status_code=400, detail="Project code already exists")
|
||||||
|
|
@ -41,20 +41,20 @@ def create_project(project: schemas.ProjectCreate, db: Session = Depends(get_db)
|
||||||
db.refresh(db_project)
|
db.refresh(db_project)
|
||||||
return db_project
|
return db_project
|
||||||
|
|
||||||
@router.get("/", response_model=List[schemas.Project])
|
@router.get("/", response_model=List[app.schemas.Project])
|
||||||
def read_projects(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
|
def read_projects(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
|
||||||
projects = db.query(Project).offset(skip).limit(limit).all()
|
projects = db.query(Project).offset(skip).limit(limit).all()
|
||||||
return projects
|
return projects
|
||||||
|
|
||||||
@router.get("/{project_id}", response_model=schemas.Project)
|
@router.get("/{project_id}", response_model=app.schemas.Project)
|
||||||
def read_project(project_id: int, db: Session = Depends(get_db)):
|
def read_project(project_id: int, db: Session = Depends(get_db)):
|
||||||
db_project = db.query(Project).filter(Project.id == project_id).first()
|
db_project = db.query(Project).filter(Project.id == project_id).first()
|
||||||
if db_project is None:
|
if db_project is None:
|
||||||
raise HTTPException(status_code=404, detail="Project not found")
|
raise HTTPException(status_code=404, detail="Project not found")
|
||||||
return db_project
|
return db_project
|
||||||
|
|
||||||
@router.put("/{project_id}", response_model=schemas.Project)
|
@router.put("/{project_id}", response_model=app.schemas.Project)
|
||||||
def update_project(project_id: int, project: schemas.ProjectCreate, db: Session = Depends(get_db)):
|
def update_project(project_id: int, project: app.schemas.ProjectCreate, db: Session = Depends(get_db)):
|
||||||
db_project = db.query(Project).filter(Project.id == project_id).first()
|
db_project = db.query(Project).filter(Project.id == project_id).first()
|
||||||
if db_project is None:
|
if db_project is None:
|
||||||
raise HTTPException(status_code=404, detail="Project not found")
|
raise HTTPException(status_code=404, detail="Project not found")
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from typing import List
|
from typing import List
|
||||||
from database import get_db
|
from app.db.database import get_db
|
||||||
from models import Specialty
|
from app.models.models import Specialty
|
||||||
from security import get_current_active_user
|
from app.security import get_current_active_user
|
||||||
import schemas
|
import app.schemas
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/specialties",
|
prefix="/specialties",
|
||||||
|
|
@ -12,6 +12,6 @@ router = APIRouter(
|
||||||
dependencies=[Depends(get_current_active_user)]
|
dependencies=[Depends(get_current_active_user)]
|
||||||
)
|
)
|
||||||
|
|
||||||
@router.get("/", response_model=List[schemas.Specialty])
|
@router.get("/", response_model=List[app.schemas.Specialty])
|
||||||
def read_specialties(db: Session = Depends(get_db)):
|
def read_specialties(db: Session = Depends(get_db)):
|
||||||
return db.query(Specialty).all()
|
return db.query(Specialty).all()
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import os
|
import os
|
||||||
from fastapi import APIRouter, Depends, UploadFile, File, HTTPException
|
from fastapi import APIRouter, Depends, UploadFile, File, HTTPException
|
||||||
from security import get_current_active_user
|
|
||||||
import google.generativeai as genai
|
import google.generativeai as genai
|
||||||
import tempfile
|
import tempfile
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
from app.security import get_current_active_user
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from typing import List
|
from typing import List
|
||||||
from database import get_db
|
from app.db.database import get_db
|
||||||
from models import User
|
from app.models.models import User
|
||||||
from security import get_password_hash, get_current_active_user
|
from app.security import get_password_hash, get_current_active_user
|
||||||
from services import users
|
from app.services import users
|
||||||
from schemas import UserCreate, User
|
from app.schemas import UserCreate, User
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/users",
|
prefix="/users",
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from pydantic import BaseModel, EmailStr, field_validator
|
from pydantic import BaseModel, EmailStr, field_validator
|
||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from models import UserRole, ActivityType, NCLevel, NCType
|
from app.models.models import UserRole, ActivityType, NCLevel, NCType
|
||||||
|
|
||||||
# Token
|
# Token
|
||||||
class Token(BaseModel):
|
class Token(BaseModel):
|
||||||
|
|
@ -5,9 +5,9 @@ from passlib.context import CryptContext
|
||||||
from fastapi import Depends, HTTPException, status
|
from fastapi import Depends, HTTPException, status
|
||||||
from fastapi.security import OAuth2PasswordBearer
|
from fastapi.security import OAuth2PasswordBearer
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from database import get_db
|
from app.db.database import get_db
|
||||||
from models import User
|
from app.models.models import User
|
||||||
import schemas
|
import app.schemas
|
||||||
|
|
||||||
# Secret key for JWT (should be in env vars in production)
|
# Secret key for JWT (should be in env vars in production)
|
||||||
SECRET_KEY = "super_secret_key_for_fritos_fresh_supervision_system"
|
SECRET_KEY = "super_secret_key_for_fritos_fresh_supervision_system"
|
||||||
|
|
@ -44,7 +44,7 @@ async def get_current_user(token: str = Depends(oauth2_scheme), db: Session = De
|
||||||
email: str = payload.get("sub")
|
email: str = payload.get("sub")
|
||||||
if email is None:
|
if email is None:
|
||||||
raise credentials_exception
|
raise credentials_exception
|
||||||
token_data = schemas.TokenData(email=email)
|
token_data = app.schemas.TokenData(email=email)
|
||||||
except JWTError:
|
except JWTError:
|
||||||
raise credentials_exception
|
raise credentials_exception
|
||||||
user = db.query(User).filter(User.email == token_data.email).first()
|
user = db.query(User).filter(User.email == token_data.email).first()
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,120 @@
|
||||||
|
import os
|
||||||
|
import uuid
|
||||||
|
import shutil
|
||||||
|
import datetime
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from typing import Optional
|
||||||
|
from fastapi import HTTPException, File, BackgroundTasks, UploadFile
|
||||||
|
from app.models.models import Activity, User, Evidence
|
||||||
|
from app.schemas import ActivityCreate, ActivityUpdate
|
||||||
|
from app.db.database import get_db
|
||||||
|
from app.security import get_current_active_user
|
||||||
|
|
||||||
|
UPLOAD_DIR = "uploads"
|
||||||
|
|
||||||
|
class ActivityService:
|
||||||
|
def __init__(self, db: Session, current_user: User):
|
||||||
|
self._db = db
|
||||||
|
self._current_user = current_user
|
||||||
|
|
||||||
|
def get_activities(self, project_id: Optional[int] = None,
|
||||||
|
specialty_id: Optional[int] = None,
|
||||||
|
skip: int = 0,
|
||||||
|
limit: int = 100):
|
||||||
|
|
||||||
|
query = self._db.query(Activity)
|
||||||
|
if project_id:
|
||||||
|
query = query.filter(Activity.project_id == project_id)
|
||||||
|
if specialty_id:
|
||||||
|
query = query.filter(Activity.specialty_id == specialty_id)
|
||||||
|
|
||||||
|
activities = query.offset(skip).limit(limit).all()
|
||||||
|
return activities
|
||||||
|
|
||||||
|
def get_activity(self, activity_id: int):
|
||||||
|
db_activity = self._db.query(Activity).filter(Activity.id == activity_id).first()
|
||||||
|
if db_activity is None:
|
||||||
|
raise HTTPException(status_code=404, detail="Activity not found")
|
||||||
|
return db_activity
|
||||||
|
|
||||||
|
def create_activity(self, activity: ActivityCreate):
|
||||||
|
db_activity = Activity(
|
||||||
|
**activity.dict(),
|
||||||
|
user_id=self._current_user.id
|
||||||
|
)
|
||||||
|
self._db.add(db_activity)
|
||||||
|
self._db.commit()
|
||||||
|
self._db.refresh(db_activity)
|
||||||
|
return db_activity
|
||||||
|
|
||||||
|
def update_activity(self, activity_id: int, activity: ActivityUpdate):
|
||||||
|
db_activity = self._db.query(Activity).filter(Activity.id == activity_id).first()
|
||||||
|
if not db_activity:
|
||||||
|
raise HTTPException(status_code=404, detail="Activity not found")
|
||||||
|
|
||||||
|
update_data = activity.dict(exclude_unset=True)
|
||||||
|
for key, value in update_data.items():
|
||||||
|
setattr(db_activity, key, value)
|
||||||
|
|
||||||
|
self._db.commit()
|
||||||
|
self._db.refresh(db_activity)
|
||||||
|
return db_activity
|
||||||
|
|
||||||
|
def upload_evidence(
|
||||||
|
self,
|
||||||
|
activity_id: int,
|
||||||
|
background_tasks: BackgroundTasks,
|
||||||
|
file: UploadFile = File(...),
|
||||||
|
description: Optional[str] = None,
|
||||||
|
captured_at: Optional[str] = None):
|
||||||
|
|
||||||
|
# Verify activity exists
|
||||||
|
db_activity = self._db.query(Activity).filter(Activity.id == activity_id).first()
|
||||||
|
if not db_activity:
|
||||||
|
raise HTTPException(status_code=404, detail="Activity not found")
|
||||||
|
|
||||||
|
# Generate unique filename
|
||||||
|
file_ext = os.path.splitext(file.filename)[1]
|
||||||
|
unique_filename = f"{uuid.uuid4()}{file_ext}"
|
||||||
|
file_path = os.path.join(UPLOAD_DIR, unique_filename)
|
||||||
|
|
||||||
|
# Save file
|
||||||
|
if not os.path.exists(UPLOAD_DIR):
|
||||||
|
os.makedirs(UPLOAD_DIR)
|
||||||
|
|
||||||
|
with open(file_path, "wb") as buffer:
|
||||||
|
shutil.copyfileobj(file.file, buffer)
|
||||||
|
|
||||||
|
db_captured_at = None
|
||||||
|
if captured_at:
|
||||||
|
try:
|
||||||
|
db_captured_at = datetime.datetime.fromisoformat(captured_at.replace('Z', '+00:00'))
|
||||||
|
except:
|
||||||
|
db_captured_at = datetime.datetime.utcnow()
|
||||||
|
else:
|
||||||
|
db_captured_at = datetime.datetime.utcnow()
|
||||||
|
|
||||||
|
# Determine transcription status
|
||||||
|
initial_status = "none"
|
||||||
|
if file.content_type and "audio" in file.content_type:
|
||||||
|
initial_status = "pending"
|
||||||
|
|
||||||
|
# Save to database
|
||||||
|
db_evidence = Evidence(
|
||||||
|
activity_id=activity_id,
|
||||||
|
file_path=file_path,
|
||||||
|
media_type=file.content_type,
|
||||||
|
description=description,
|
||||||
|
captured_at=db_captured_at,
|
||||||
|
transcription_status=initial_status
|
||||||
|
)
|
||||||
|
self._db.add(db_evidence)
|
||||||
|
self._db.commit()
|
||||||
|
self._db.refresh(db_evidence)
|
||||||
|
|
||||||
|
# If it's audio, queue transcription
|
||||||
|
if initial_status == "pending":
|
||||||
|
from app.services.transcription_worker import process_transcription
|
||||||
|
background_tasks.add_task(process_transcription, db_evidence.id)
|
||||||
|
|
||||||
|
return db_evidence
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from models import Evidence
|
|
||||||
import google.generativeai as genai
|
import google.generativeai as genai
|
||||||
from database import SessionLocal
|
from app.models import Evidence
|
||||||
|
from app.database import SessionLocal
|
||||||
|
|
||||||
def process_transcription(evidence_id: int):
|
def process_transcription(evidence_id: int):
|
||||||
"""
|
"""
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from models import User
|
|
||||||
from security import get_password_hash
|
|
||||||
from schemas import UserCreate
|
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
|
from app.models.models import User
|
||||||
|
from app.security import get_password_hash
|
||||||
|
from app.schemas import UserCreate
|
||||||
|
|
||||||
def get_users(db: Session, skip: int = 0, limit: int = 100):
|
def get_users(db: Session, skip: int = 0, limit: int = 100):
|
||||||
return db.query(User).offset(skip).limit(limit).all()
|
return db.query(User).offset(skip).limit(limit).all()
|
||||||
|
|
@ -11,7 +11,7 @@ if sys.version_info < (3, 10):
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from routers import auth, users, projects, activities, specialties, contractors, transcription, non_conformities, guest
|
from app.routers import auth, users, projects, activities, specialties, contractors, transcription, non_conformities, guest
|
||||||
import os
|
import os
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,22 @@
|
||||||
alembic==1.16.5
|
alembic==1.17.2
|
||||||
annotated-doc==0.0.4
|
annotated-doc==0.0.4
|
||||||
annotated-types==0.7.0
|
annotated-types==0.7.0
|
||||||
anyio==4.12.0
|
anyio==4.12.0
|
||||||
bcrypt==3.2.2
|
bcrypt==5.0.0
|
||||||
cachetools==6.2.4
|
cachetools==6.2.4
|
||||||
certifi==2025.11.12
|
certifi==2025.11.12
|
||||||
cffi==2.0.0
|
cffi==2.0.0
|
||||||
charset-normalizer==3.4.4
|
charset-normalizer==3.4.4
|
||||||
click==8.1.8
|
click==8.3.1
|
||||||
cryptography==46.0.3
|
cryptography==46.0.3
|
||||||
dnspython==2.7.0
|
dnspython==2.8.0
|
||||||
ecdsa==0.19.1
|
ecdsa==0.19.1
|
||||||
email-validator==2.3.0
|
email-validator==2.3.0
|
||||||
exceptiongroup==1.3.1
|
exceptiongroup==1.3.1
|
||||||
fastapi==0.125.0
|
fastapi==0.128.0
|
||||||
fastapi-cli==0.0.16
|
fastapi-cli==0.0.20
|
||||||
fastapi-cloud-cli==0.7.0
|
|
||||||
fastar==0.8.0
|
|
||||||
google-ai-generativelanguage==0.6.15
|
google-ai-generativelanguage==0.6.15
|
||||||
google-api-core==2.28.1
|
google-api-core==2.25.2
|
||||||
google-api-python-client==2.187.0
|
google-api-python-client==2.187.0
|
||||||
google-auth==2.45.0
|
google-auth==2.45.0
|
||||||
google-auth-httplib2==0.3.0
|
google-auth-httplib2==0.3.0
|
||||||
|
|
@ -32,18 +30,12 @@ httplib2==0.31.0
|
||||||
httptools==0.7.1
|
httptools==0.7.1
|
||||||
httpx==0.28.1
|
httpx==0.28.1
|
||||||
idna==3.11
|
idna==3.11
|
||||||
iniconfig==2.1.0
|
|
||||||
Jinja2==3.1.6
|
|
||||||
Mako==1.3.10
|
Mako==1.3.10
|
||||||
markdown-it-py==3.0.0
|
markdown-it-py==4.0.0
|
||||||
MarkupSafe==3.0.3
|
MarkupSafe==3.0.3
|
||||||
mdurl==0.1.2
|
mdurl==0.1.2
|
||||||
numpy==2.0.2
|
numpy==2.2.6
|
||||||
opencv-python-headless==4.12.0.88
|
opencv-python-headless==4.12.0.88
|
||||||
packaging==25.0
|
|
||||||
passlib==1.7.4
|
|
||||||
pillow==11.3.0
|
|
||||||
pluggy==1.6.0
|
|
||||||
proto-plus==1.27.0
|
proto-plus==1.27.0
|
||||||
protobuf==5.29.5
|
protobuf==5.29.5
|
||||||
psycopg2-binary==2.9.11
|
psycopg2-binary==2.9.11
|
||||||
|
|
@ -53,30 +45,26 @@ pycparser==2.23
|
||||||
pydantic==2.12.5
|
pydantic==2.12.5
|
||||||
pydantic_core==2.41.5
|
pydantic_core==2.41.5
|
||||||
Pygments==2.19.2
|
Pygments==2.19.2
|
||||||
pyparsing==3.2.5
|
pyparsing==3.3.1
|
||||||
pytest==8.4.2
|
|
||||||
python-dotenv==1.2.1
|
python-dotenv==1.2.1
|
||||||
python-jose==3.5.0
|
python-jose==3.5.0
|
||||||
python-multipart==0.0.20
|
python-multipart==0.0.21
|
||||||
PyYAML==6.0.3
|
PyYAML==6.0.3
|
||||||
requests==2.32.5
|
requests==2.32.5
|
||||||
rich==14.2.0
|
rich==14.2.0
|
||||||
rich-toolkit==0.17.1
|
rich-toolkit==0.17.1
|
||||||
rignore==0.7.6
|
|
||||||
rsa==4.9.1
|
rsa==4.9.1
|
||||||
sentry-sdk==2.48.0
|
|
||||||
shellingham==1.5.4
|
shellingham==1.5.4
|
||||||
six==1.17.0
|
six==1.17.0
|
||||||
SQLAlchemy==2.0.45
|
SQLAlchemy==2.0.45
|
||||||
starlette==0.49.3
|
starlette==0.50.0
|
||||||
tomli==2.3.0
|
|
||||||
tqdm==4.67.1
|
tqdm==4.67.1
|
||||||
typer==0.20.0
|
typer==0.21.0
|
||||||
typing-inspection==0.4.2
|
typing-inspection==0.4.2
|
||||||
typing_extensions==4.15.0
|
typing_extensions==4.15.0
|
||||||
uritemplate==4.2.0
|
uritemplate==4.2.0
|
||||||
urllib3==2.6.2
|
urllib3==2.6.2
|
||||||
uvicorn==0.38.0
|
uvicorn==0.40.0
|
||||||
uvloop==0.22.1
|
uvloop==0.22.1
|
||||||
watchfiles==1.1.1
|
watchfiles==1.1.1
|
||||||
websockets==15.0.1
|
websockets==15.0.1
|
||||||
|
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
from typing import Optional
|
|
||||||
from models import Activity, User
|
|
||||||
from schemas import ActivityCreate
|
|
||||||
from fastapi import HTTPException
|
|
||||||
|
|
||||||
def get_activities(db: Session, current_user: User, project_id: Optional[int] = None,
|
|
||||||
specialty_id: Optional[int] = None,
|
|
||||||
skip: int = 0,
|
|
||||||
limit: int = 100):
|
|
||||||
|
|
||||||
query = db.query(Activity)
|
|
||||||
if project_id:
|
|
||||||
query = query.filter(Activity.project_id == project_id)
|
|
||||||
if specialty_id:
|
|
||||||
query = query.filter(Activity.specialty_id == specialty_id)
|
|
||||||
|
|
||||||
activities = query.offset(skip).limit(limit).all()
|
|
||||||
return activities
|
|
||||||
|
|
||||||
def get_activity(db: Session, activity_id: int):
|
|
||||||
db_activity = db.query(Activity).filter(Activity.id == activity_id).first()
|
|
||||||
if db_activity is None:
|
|
||||||
raise HTTPException(status_code=404, detail="Activity not found")
|
|
||||||
return db_activity
|
|
||||||
|
|
||||||
def create_activity(db: Session, activity: ActivityCreate, current_user: User):
|
|
||||||
db_activity = Activity(
|
|
||||||
**activity.dict(),
|
|
||||||
user_id=current_user.id
|
|
||||||
)
|
|
||||||
db.add(db_activity)
|
|
||||||
db.commit()
|
|
||||||
db.refresh(db_activity)
|
|
||||||
return db_activity
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
export const environment = {
|
export const environment = {
|
||||||
apiUrl: 'http://192.168.1.74:8000'
|
apiUrl: 'http://192.168.1.76:8000'
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
export const environment = {
|
export const environment = {
|
||||||
apiUrl: 'http://192.168.1.74:8000'
|
apiUrl: 'http://192.168.1.76:8000'
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue