from flask import Blueprint, request, jsonify, send_from_directory, current_app from flask_login import login_required, current_user from werkzeug.utils import secure_filename import sys import os from datetime import datetime, date # 添加父目录到路径 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from models import db, ResearchProject, ResearchNote, ResearchTask, ResearchFile, User bp = Blueprint('research', __name__) def allowed_file(filename, allowed_extensions): """检查文件扩展名是否允许""" return '.' in filename and filename.rsplit('.', 1)[1].lower() in allowed_extensions # 研究项目管理 @bp.route('/projects', methods=['GET']) @login_required def get_projects(): """获取研究项目列表(实验室成员可查看所有)""" status = request.args.get('status') owner_id = request.args.get('owner_id') # 可选:筛选特定用户的项目 query = ResearchProject.query if status: query = query.filter_by(status=status) if owner_id: query = query.filter_by(owner_id=owner_id) projects = query.order_by(ResearchProject.created_at.desc()).all() return jsonify({ 'projects': [project.to_dict() for project in projects] }), 200 @bp.route('/projects/', methods=['GET']) @login_required def get_project(project_id): """获取单个研究项目详情""" project = ResearchProject.query.get_or_404(project_id) # 获取笔记、任务、文件 notes = ResearchNote.query.filter_by(project_id=project_id).order_by(ResearchNote.created_at.desc()).all() tasks = ResearchTask.query.filter_by(project_id=project_id).order_by(ResearchTask.created_at.desc()).all() files = ResearchFile.query.filter_by(project_id=project_id).order_by(ResearchFile.uploaded_at.desc()).all() result = project.to_dict() result['notes'] = [note.to_dict() for note in notes] result['tasks'] = [task.to_dict() for task in tasks] result['files'] = [f.to_dict() for f in files] return jsonify({'project': result}), 200 @bp.route('/projects', methods=['POST']) @login_required def create_project(): """创建研究项目(需要登录)""" data = request.get_json() start_date = datetime.strptime(data['start_date'], '%Y-%m-%d').date() if data.get('start_date') else None end_date = datetime.strptime(data['end_date'], '%Y-%m-%d').date() if data.get('end_date') else None tags_str = ','.join(data.get('tags', [])) if isinstance(data.get('tags'), list) else data.get('tags', '') project = ResearchProject( title=data.get('title'), description=data.get('description'), start_date=start_date, end_date=end_date, owner_id=current_user.id, status=data.get('status', 'active'), progress=data.get('progress', 0), tags=tags_str ) db.session.add(project) db.session.commit() return jsonify({ 'message': 'Project created successfully', 'project': project.to_dict() }), 201 @bp.route('/projects/', methods=['PUT']) @login_required def update_project(project_id): """更新研究项目(只有创建者可以编辑)""" project = ResearchProject.query.get_or_404(project_id) # 检查权限 if project.owner_id != current_user.id: return jsonify({'error': 'Only the owner can edit this project'}), 403 data = request.get_json() if 'title' in data: project.title = data['title'] if 'description' in data: project.description = data['description'] if 'start_date' in data: project.start_date = datetime.strptime(data['start_date'], '%Y-%m-%d').date() if data['start_date'] else None if 'end_date' in data: project.end_date = datetime.strptime(data['end_date'], '%Y-%m-%d').date() if data['end_date'] else None if 'status' in data: project.status = data['status'] if 'progress' in data: project.progress = data['progress'] if 'tags' in data: tags_str = ','.join(data['tags']) if isinstance(data['tags'], list) else data['tags'] project.tags = tags_str project.updated_at = datetime.utcnow() db.session.commit() return jsonify({ 'message': 'Project updated successfully', 'project': project.to_dict() }), 200 @bp.route('/projects/', methods=['DELETE']) @login_required def delete_project(project_id): """删除研究项目(只有创建者可以删除)""" project = ResearchProject.query.get_or_404(project_id) # 检查权限 if project.owner_id != current_user.id: return jsonify({'error': 'Only the owner can delete this project'}), 403 db.session.delete(project) db.session.commit() return jsonify({'message': 'Project deleted successfully'}), 200 # 研究笔记管理 @bp.route('/projects//notes', methods=['GET']) @login_required def get_notes(project_id): """获取项目笔记列表""" note_type = request.args.get('type') query = ResearchNote.query.filter_by(project_id=project_id) if note_type: query = query.filter_by(note_type=note_type) notes = query.order_by(ResearchNote.created_at.desc()).all() return jsonify({ 'notes': [note.to_dict() for note in notes] }), 200 @bp.route('/projects//notes', methods=['POST']) @login_required def create_note(project_id): """创建研究笔记(需要登录)""" project = ResearchProject.query.get_or_404(project_id) data = request.get_json() note = ResearchNote( project_id=project_id, title=data.get('title'), content=data.get('content'), author_id=current_user.id, note_type=data.get('note_type', 'note') ) db.session.add(note) db.session.commit() return jsonify({ 'message': 'Note created successfully', 'note': note.to_dict() }), 201 @bp.route('/notes/', methods=['PUT']) @login_required def update_note(note_id): """更新研究笔记(只有创建者可以编辑)""" note = ResearchNote.query.get_or_404(note_id) # 检查权限 if note.author_id != current_user.id: return jsonify({'error': 'Only the author can edit this note'}), 403 data = request.get_json() if 'title' in data: note.title = data['title'] if 'content' in data: note.content = data['content'] if 'note_type' in data: note.note_type = data['note_type'] note.updated_at = datetime.utcnow() db.session.commit() return jsonify({ 'message': 'Note updated successfully', 'note': note.to_dict() }), 200 @bp.route('/notes/', methods=['DELETE']) @login_required def delete_note(note_id): """删除研究笔记(只有创建者可以删除)""" note = ResearchNote.query.get_or_404(note_id) # 检查权限 if note.author_id != current_user.id: return jsonify({'error': 'Only the author can delete this note'}), 403 db.session.delete(note) db.session.commit() return jsonify({'message': 'Note deleted successfully'}), 200 # 研究任务管理 @bp.route('/projects//tasks', methods=['GET']) @login_required def get_tasks(project_id): """获取项目任务列表""" status = request.args.get('status') assignee_id = request.args.get('assignee_id') query = ResearchTask.query.filter_by(project_id=project_id) if status: query = query.filter_by(status=status) if assignee_id: query = query.filter_by(assignee_id=assignee_id) tasks = query.order_by(ResearchTask.due_date, ResearchTask.priority).all() return jsonify({ 'tasks': [task.to_dict() for task in tasks] }), 200 @bp.route('/projects//tasks', methods=['POST']) @login_required def create_task(project_id): """创建研究任务(项目创建者可以创建)""" project = ResearchProject.query.get_or_404(project_id) # 只有项目创建者可以创建任务 if project.owner_id != current_user.id: return jsonify({'error': 'Only the project owner can create tasks'}), 403 data = request.get_json() due_date = datetime.strptime(data['due_date'], '%Y-%m-%d').date() if data.get('due_date') else None task = ResearchTask( project_id=project_id, title=data.get('title'), description=data.get('description'), assignee_id=data.get('assignee_id'), due_date=due_date, status=data.get('status', 'todo'), priority=data.get('priority', 'medium') ) db.session.add(task) db.session.commit() return jsonify({ 'message': 'Task created successfully', 'task': task.to_dict() }), 201 @bp.route('/tasks/', methods=['PUT']) @login_required def update_task(task_id): """更新研究任务(项目创建者或任务分配者可以编辑)""" task = ResearchTask.query.get_or_404(task_id) project = ResearchProject.query.get(task.project_id) # 检查权限:项目创建者或任务分配者 if project.owner_id != current_user.id and task.assignee_id != current_user.id: return jsonify({'error': 'Only the project owner or assignee can edit this task'}), 403 data = request.get_json() if 'title' in data: task.title = data['title'] if 'description' in data: task.description = data['description'] if 'assignee_id' in data: task.assignee_id = data['assignee_id'] if 'due_date' in data: task.due_date = datetime.strptime(data['due_date'], '%Y-%m-%d').date() if data['due_date'] else None if 'status' in data: task.status = data['status'] if 'priority' in data: task.priority = data['priority'] task.updated_at = datetime.utcnow() db.session.commit() return jsonify({ 'message': 'Task updated successfully', 'task': task.to_dict() }), 200 @bp.route('/tasks/', methods=['DELETE']) @login_required def delete_task(task_id): """删除研究任务(只有项目创建者可以删除)""" task = ResearchTask.query.get_or_404(task_id) project = ResearchProject.query.get(task.project_id) # 检查权限 if project.owner_id != current_user.id: return jsonify({'error': 'Only the project owner can delete this task'}), 403 db.session.delete(task) db.session.commit() return jsonify({'message': 'Task deleted successfully'}), 200 # 研究文件管理 @bp.route('/projects//files', methods=['POST']) @login_required def upload_research_file(project_id): """上传研究文件(需要登录)""" project = ResearchProject.query.get_or_404(project_id) if 'file' not in request.files: return jsonify({'error': 'No file provided'}), 400 file = request.files['file'] if file.filename == '': return jsonify({'error': 'No file selected'}), 400 if file and allowed_file(file.filename, {'pdf', 'doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'zip', 'rar', 'txt', 'csv', 'json', 'py', 'ipynb'}): filename = secure_filename(file.filename) uploads_dir = current_app.config['UPLOAD_FOLDER'] project_dir = os.path.join(uploads_dir, 'research', str(project_id)) os.makedirs(project_dir, exist_ok=True) filepath = os.path.join(project_dir, filename) file.save(filepath) research_file = ResearchFile( project_id=project_id, name=request.form.get('name', filename), file_path=f'/uploads/research/{project_id}/{filename}', file_size=os.path.getsize(filepath), file_type=filename.rsplit('.', 1)[1].lower(), uploader_id=current_user.id ) db.session.add(research_file) db.session.commit() return jsonify({ 'message': 'File uploaded successfully', 'file': research_file.to_dict() }), 201 return jsonify({'error': 'Invalid file type'}), 400 @bp.route('/projects//files/', methods=['GET']) @login_required def download_research_file(project_id, file_id): """下载研究文件""" research_file = ResearchFile.query.filter_by( project_id=project_id, id=file_id ).first_or_404() uploads_dir = current_app.config['UPLOAD_FOLDER'] file_path = os.path.join(uploads_dir, 'research', str(project_id), os.path.basename(research_file.file_path)) return send_from_directory( os.path.dirname(file_path), os.path.basename(file_path), as_attachment=True ) @bp.route('/projects//files/', methods=['DELETE']) @login_required def delete_research_file(project_id, file_id): """删除研究文件(只有上传者可以删除)""" research_file = ResearchFile.query.filter_by( project_id=project_id, id=file_id ).first_or_404() # 检查权限 if research_file.uploader_id != current_user.id: return jsonify({'error': 'Only the uploader can delete this file'}), 403 db.session.delete(research_file) db.session.commit() return jsonify({'message': 'File deleted successfully'}), 200