Initial commit
This commit is contained in:
commit
983cee0320
322 changed files with 57174 additions and 0 deletions
222
backup/fina-1/app/routes/documents.py
Normal file
222
backup/fina-1/app/routes/documents.py
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
from flask import Blueprint, request, jsonify, send_file, current_app
|
||||
from flask_login import login_required, current_user
|
||||
from app import db
|
||||
from app.models import Document
|
||||
from werkzeug.utils import secure_filename
|
||||
import os
|
||||
import mimetypes
|
||||
from datetime import datetime
|
||||
|
||||
bp = Blueprint('documents', __name__, url_prefix='/api/documents')
|
||||
|
||||
# Max file size: 10MB
|
||||
MAX_FILE_SIZE = 10 * 1024 * 1024
|
||||
|
||||
# Allowed file types for documents
|
||||
ALLOWED_DOCUMENT_TYPES = {
|
||||
'pdf': 'application/pdf',
|
||||
'csv': 'text/csv',
|
||||
'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'xls': 'application/vnd.ms-excel',
|
||||
'png': 'image/png',
|
||||
'jpg': 'image/jpeg',
|
||||
'jpeg': 'image/jpeg'
|
||||
}
|
||||
|
||||
def allowed_document(filename):
|
||||
"""Check if file type is allowed"""
|
||||
return '.' in filename and \
|
||||
filename.rsplit('.', 1)[1].lower() in ALLOWED_DOCUMENT_TYPES.keys()
|
||||
|
||||
def get_file_type_icon(file_type):
|
||||
"""Get material icon name for file type"""
|
||||
icons = {
|
||||
'pdf': 'picture_as_pdf',
|
||||
'csv': 'table_view',
|
||||
'xlsx': 'table_view',
|
||||
'xls': 'table_view',
|
||||
'png': 'image',
|
||||
'jpg': 'image',
|
||||
'jpeg': 'image'
|
||||
}
|
||||
return icons.get(file_type.lower(), 'description')
|
||||
|
||||
@bp.route('/', methods=['GET'])
|
||||
@login_required
|
||||
def get_documents():
|
||||
"""
|
||||
Get all documents for current user
|
||||
Security: Filters by current_user.id
|
||||
"""
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = request.args.get('per_page', 10, type=int)
|
||||
search = request.args.get('search', '')
|
||||
|
||||
# Security: Only get documents for current user
|
||||
query = Document.query.filter_by(user_id=current_user.id)
|
||||
|
||||
if search:
|
||||
query = query.filter(Document.original_filename.ilike(f'%{search}%'))
|
||||
|
||||
pagination = query.order_by(Document.created_at.desc()).paginate(
|
||||
page=page, per_page=per_page, error_out=False
|
||||
)
|
||||
|
||||
return jsonify({
|
||||
'documents': [doc.to_dict() for doc in pagination.items],
|
||||
'total': pagination.total,
|
||||
'pages': pagination.pages,
|
||||
'current_page': page
|
||||
})
|
||||
|
||||
|
||||
@bp.route('/', methods=['POST'])
|
||||
@login_required
|
||||
def upload_document():
|
||||
"""
|
||||
Upload a new document
|
||||
Security: Associates document with current_user.id
|
||||
"""
|
||||
if 'file' not in request.files:
|
||||
return jsonify({'success': False, 'message': 'No file provided'}), 400
|
||||
|
||||
file = request.files['file']
|
||||
|
||||
if not file or not file.filename:
|
||||
return jsonify({'success': False, 'message': 'No file selected'}), 400
|
||||
|
||||
if not allowed_document(file.filename):
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': 'Invalid file type. Allowed: PDF, CSV, XLS, XLSX, PNG, JPG'
|
||||
}), 400
|
||||
|
||||
# Check file size
|
||||
file.seek(0, os.SEEK_END)
|
||||
file_size = file.tell()
|
||||
file.seek(0)
|
||||
|
||||
if file_size > MAX_FILE_SIZE:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': f'File too large. Maximum size: {MAX_FILE_SIZE // (1024*1024)}MB'
|
||||
}), 400
|
||||
|
||||
# Generate secure filename
|
||||
original_filename = secure_filename(file.filename)
|
||||
file_ext = original_filename.rsplit('.', 1)[1].lower()
|
||||
timestamp = datetime.utcnow().timestamp()
|
||||
filename = f"{current_user.id}_{timestamp}_{original_filename}"
|
||||
|
||||
# Create documents directory if it doesn't exist
|
||||
documents_dir = os.path.join(current_app.config['UPLOAD_FOLDER'], 'documents')
|
||||
os.makedirs(documents_dir, exist_ok=True)
|
||||
|
||||
# Save file
|
||||
file_path = os.path.join(documents_dir, filename)
|
||||
file.save(file_path)
|
||||
|
||||
# Get document category from form data
|
||||
document_category = request.form.get('category', 'Other')
|
||||
|
||||
# Create document record - Security: user_id is current_user.id
|
||||
document = Document(
|
||||
filename=filename,
|
||||
original_filename=original_filename,
|
||||
file_path=file_path,
|
||||
file_size=file_size,
|
||||
file_type=file_ext.upper(),
|
||||
mime_type=ALLOWED_DOCUMENT_TYPES.get(file_ext, 'application/octet-stream'),
|
||||
document_category=document_category,
|
||||
status='uploaded',
|
||||
user_id=current_user.id
|
||||
)
|
||||
|
||||
db.session.add(document)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': 'Document uploaded successfully',
|
||||
'document': document.to_dict()
|
||||
}), 201
|
||||
|
||||
|
||||
@bp.route('/<int:document_id>/download', methods=['GET'])
|
||||
@login_required
|
||||
def download_document(document_id):
|
||||
"""
|
||||
Download a document
|
||||
Security: Checks document belongs to current_user
|
||||
"""
|
||||
# Security: Filter by user_id
|
||||
document = Document.query.filter_by(id=document_id, user_id=current_user.id).first()
|
||||
|
||||
if not document:
|
||||
return jsonify({'success': False, 'message': 'Document not found'}), 404
|
||||
|
||||
if not os.path.exists(document.file_path):
|
||||
return jsonify({'success': False, 'message': 'File not found on server'}), 404
|
||||
|
||||
return send_file(
|
||||
document.file_path,
|
||||
mimetype=document.mime_type,
|
||||
as_attachment=True,
|
||||
download_name=document.original_filename
|
||||
)
|
||||
|
||||
|
||||
@bp.route('/<int:document_id>', methods=['DELETE'])
|
||||
@login_required
|
||||
def delete_document(document_id):
|
||||
"""
|
||||
Delete a document
|
||||
Security: Checks document belongs to current_user
|
||||
"""
|
||||
# Security: Filter by user_id
|
||||
document = Document.query.filter_by(id=document_id, user_id=current_user.id).first()
|
||||
|
||||
if not document:
|
||||
return jsonify({'success': False, 'message': 'Document not found'}), 404
|
||||
|
||||
# Delete physical file
|
||||
if os.path.exists(document.file_path):
|
||||
try:
|
||||
os.remove(document.file_path)
|
||||
except Exception as e:
|
||||
print(f"Error deleting file: {e}")
|
||||
|
||||
# Delete database record
|
||||
db.session.delete(document)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({'success': True, 'message': 'Document deleted successfully'})
|
||||
|
||||
|
||||
@bp.route('/<int:document_id>/status', methods=['PUT'])
|
||||
@login_required
|
||||
def update_document_status(document_id):
|
||||
"""
|
||||
Update document status (e.g., mark as analyzed)
|
||||
Security: Checks document belongs to current_user
|
||||
"""
|
||||
# Security: Filter by user_id
|
||||
document = Document.query.filter_by(id=document_id, user_id=current_user.id).first()
|
||||
|
||||
if not document:
|
||||
return jsonify({'success': False, 'message': 'Document not found'}), 404
|
||||
|
||||
data = request.get_json()
|
||||
new_status = data.get('status')
|
||||
|
||||
if new_status not in ['uploaded', 'processing', 'analyzed', 'error']:
|
||||
return jsonify({'success': False, 'message': 'Invalid status'}), 400
|
||||
|
||||
document.status = new_status
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': 'Document status updated',
|
||||
'document': document.to_dict()
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue