init: TEALab 2025 实验室网站项目
从百度网盘 /apps/bypy/dofile/ 下载的完整项目结构,包含: - dofile/: Flask 后端(端口 5000)+ 静态前端(端口 6002) - officefile/: 项目相关文档 - .gitignore: Python venv / 日志 / 缓存 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,342 @@
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_login import UserMixin
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
from datetime import datetime
|
||||
|
||||
db = SQLAlchemy()
|
||||
|
||||
class User(UserMixin, db.Model):
|
||||
"""用户模型"""
|
||||
__tablename__ = 'users'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(80), unique=True, nullable=False)
|
||||
email = db.Column(db.String(120), unique=True, nullable=False)
|
||||
password_hash = db.Column(db.String(255), nullable=False)
|
||||
is_admin = db.Column(db.Boolean, default=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
def set_password(self, password):
|
||||
self.password_hash = generate_password_hash(password)
|
||||
|
||||
def check_password(self, password):
|
||||
return check_password_hash(self.password_hash, password)
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'username': self.username,
|
||||
'email': self.email,
|
||||
'is_admin': self.is_admin,
|
||||
'created_at': self.created_at.isoformat()
|
||||
}
|
||||
|
||||
class Article(db.Model):
|
||||
"""文章/新闻模型"""
|
||||
__tablename__ = 'articles'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
title = db.Column(db.String(200), nullable=False)
|
||||
content = db.Column(db.Text, nullable=False)
|
||||
summary = db.Column(db.Text)
|
||||
category = db.Column(db.String(50), default='news') # news, publication, etc.
|
||||
is_protected = db.Column(db.Boolean, default=False) # 是否需要登录查看
|
||||
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
published = db.Column(db.Boolean, default=True)
|
||||
|
||||
author = db.relationship('User', backref='articles')
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'title': self.title,
|
||||
'content': self.content,
|
||||
'summary': self.summary,
|
||||
'category': self.category,
|
||||
'is_protected': self.is_protected,
|
||||
'author_id': self.author_id,
|
||||
'created_at': self.created_at.isoformat(),
|
||||
'updated_at': self.updated_at.isoformat(),
|
||||
'published': self.published
|
||||
}
|
||||
|
||||
class Resource(db.Model):
|
||||
"""资源文件模型"""
|
||||
__tablename__ = 'resources'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(200), nullable=False)
|
||||
description = db.Column(db.Text)
|
||||
file_path = db.Column(db.String(500), nullable=False)
|
||||
file_size = db.Column(db.Integer) # 文件大小(字节)
|
||||
file_type = db.Column(db.String(50)) # 文件类型
|
||||
category = db.Column(db.String(50)) # software, dataset, document, etc.
|
||||
is_protected = db.Column(db.Boolean, default=False) # 是否需要登录下载
|
||||
download_count = db.Column(db.Integer, default=0)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'name': self.name,
|
||||
'description': self.description,
|
||||
'file_path': self.file_path,
|
||||
'file_size': self.file_size,
|
||||
'file_type': self.file_type,
|
||||
'category': self.category,
|
||||
'is_protected': self.is_protected,
|
||||
'download_count': self.download_count,
|
||||
'created_at': self.created_at.isoformat(),
|
||||
'updated_at': self.updated_at.isoformat()
|
||||
}
|
||||
|
||||
class TeamMember(db.Model):
|
||||
"""团队成员模型"""
|
||||
__tablename__ = 'team_members'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(100), nullable=False)
|
||||
title = db.Column(db.String(200))
|
||||
title_en = db.Column(db.String(200))
|
||||
bio = db.Column(db.Text)
|
||||
bio_en = db.Column(db.Text)
|
||||
role = db.Column(db.String(50)) # professor, student, alumni
|
||||
email = db.Column(db.String(120))
|
||||
website = db.Column(db.String(500))
|
||||
avatar_path = db.Column(db.String(500))
|
||||
order = db.Column(db.Integer, default=0) # 排序
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'name': self.name,
|
||||
'title': self.title,
|
||||
'title_en': self.title_en,
|
||||
'bio': self.bio,
|
||||
'bio_en': self.bio_en,
|
||||
'role': self.role,
|
||||
'email': self.email,
|
||||
'website': self.website,
|
||||
'avatar_path': self.avatar_path,
|
||||
'order': self.order,
|
||||
'created_at': self.created_at.isoformat(),
|
||||
'updated_at': self.updated_at.isoformat()
|
||||
}
|
||||
|
||||
class Meeting(db.Model):
|
||||
"""组会模型"""
|
||||
__tablename__ = 'meetings'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
title = db.Column(db.String(200), nullable=False)
|
||||
description = db.Column(db.Text)
|
||||
date = db.Column(db.Date, nullable=False)
|
||||
time = db.Column(db.Time, nullable=False)
|
||||
location = db.Column(db.String(200))
|
||||
organizer_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
status = db.Column(db.String(50), default='planned') # planned, ongoing, completed, cancelled
|
||||
meeting_type = db.Column(db.String(50), default='weekly') # weekly, monthly, special
|
||||
meeting_notes = db.Column(db.Text)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
organizer = db.relationship('User', backref='organized_meetings')
|
||||
registrations = db.relationship('MeetingRegistration', backref='meeting', cascade='all, delete-orphan')
|
||||
files = db.relationship('MeetingFile', backref='meeting', cascade='all, delete-orphan')
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'title': self.title,
|
||||
'description': self.description,
|
||||
'date': self.date.isoformat() if self.date else None,
|
||||
'time': self.time.strftime('%H:%M') if self.time else None,
|
||||
'location': self.location,
|
||||
'organizer_id': self.organizer_id,
|
||||
'organizer_name': self.organizer.username if self.organizer else None,
|
||||
'status': self.status,
|
||||
'meeting_type': self.meeting_type,
|
||||
'meeting_notes': self.meeting_notes,
|
||||
'created_at': self.created_at.isoformat(),
|
||||
'updated_at': self.updated_at.isoformat(),
|
||||
'attendee_count': len(self.registrations) if self.registrations else 0
|
||||
}
|
||||
|
||||
class MeetingRegistration(db.Model):
|
||||
"""组会报名模型"""
|
||||
__tablename__ = 'meeting_registrations'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
meeting_id = db.Column(db.Integer, db.ForeignKey('meetings.id'), nullable=False)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
registered_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
status = db.Column(db.String(50), default='registered') # registered, checked_in, absent
|
||||
|
||||
user = db.relationship('User', backref='meeting_registrations')
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'meeting_id': self.meeting_id,
|
||||
'user_id': self.user_id,
|
||||
'username': self.user.username if self.user else None,
|
||||
'registered_at': self.registered_at.isoformat(),
|
||||
'status': self.status
|
||||
}
|
||||
|
||||
class MeetingFile(db.Model):
|
||||
"""组会文件模型"""
|
||||
__tablename__ = 'meeting_files'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
meeting_id = db.Column(db.Integer, db.ForeignKey('meetings.id'), nullable=False)
|
||||
name = db.Column(db.String(200), nullable=False)
|
||||
file_path = db.Column(db.String(500), nullable=False)
|
||||
file_size = db.Column(db.Integer)
|
||||
file_type = db.Column(db.String(50))
|
||||
uploader_id = db.Column(db.Integer, db.ForeignKey('users.id'))
|
||||
uploaded_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
uploader = db.relationship('User', backref='uploaded_meeting_files')
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'meeting_id': self.meeting_id,
|
||||
'name': self.name,
|
||||
'file_path': self.file_path,
|
||||
'file_size': self.file_size,
|
||||
'file_type': self.file_type,
|
||||
'uploader_id': self.uploader_id,
|
||||
'uploaded_at': self.uploaded_at.isoformat()
|
||||
}
|
||||
|
||||
class ResearchProject(db.Model):
|
||||
"""研究项目模型"""
|
||||
__tablename__ = 'research_projects'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
title = db.Column(db.String(200), nullable=False)
|
||||
description = db.Column(db.Text)
|
||||
start_date = db.Column(db.Date)
|
||||
end_date = db.Column(db.Date)
|
||||
owner_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
status = db.Column(db.String(50), default='active') # active, completed, paused, cancelled
|
||||
progress = db.Column(db.Integer, default=0) # 0-100
|
||||
tags = db.Column(db.String(500)) # 逗号分隔的标签
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
owner = db.relationship('User', backref='research_projects')
|
||||
notes = db.relationship('ResearchNote', backref='project', cascade='all, delete-orphan')
|
||||
tasks = db.relationship('ResearchTask', backref='project', cascade='all, delete-orphan')
|
||||
files = db.relationship('ResearchFile', backref='project', cascade='all, delete-orphan')
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'title': self.title,
|
||||
'description': self.description,
|
||||
'start_date': self.start_date.isoformat() if self.start_date else None,
|
||||
'end_date': self.end_date.isoformat() if self.end_date else None,
|
||||
'owner_id': self.owner_id,
|
||||
'owner_name': self.owner.username if self.owner else None,
|
||||
'status': self.status,
|
||||
'progress': self.progress,
|
||||
'tags': self.tags.split(',') if self.tags else [],
|
||||
'created_at': self.created_at.isoformat(),
|
||||
'updated_at': self.updated_at.isoformat()
|
||||
}
|
||||
|
||||
class ResearchNote(db.Model):
|
||||
"""研究笔记模型"""
|
||||
__tablename__ = 'research_notes'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
project_id = db.Column(db.Integer, db.ForeignKey('research_projects.id'), nullable=False)
|
||||
title = db.Column(db.String(200), nullable=False)
|
||||
content = db.Column(db.Text, nullable=False)
|
||||
author_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
note_type = db.Column(db.String(50), default='note') # note, experiment, literature, idea
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
author = db.relationship('User', backref='research_notes')
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'project_id': self.project_id,
|
||||
'title': self.title,
|
||||
'content': self.content,
|
||||
'author_id': self.author_id,
|
||||
'author_name': self.author.username if self.author else None,
|
||||
'note_type': self.note_type,
|
||||
'created_at': self.created_at.isoformat(),
|
||||
'updated_at': self.updated_at.isoformat()
|
||||
}
|
||||
|
||||
class ResearchTask(db.Model):
|
||||
"""研究任务模型"""
|
||||
__tablename__ = 'research_tasks'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
project_id = db.Column(db.Integer, db.ForeignKey('research_projects.id'), nullable=False)
|
||||
title = db.Column(db.String(200), nullable=False)
|
||||
description = db.Column(db.Text)
|
||||
assignee_id = db.Column(db.Integer, db.ForeignKey('users.id'))
|
||||
due_date = db.Column(db.Date)
|
||||
status = db.Column(db.String(50), default='todo') # todo, in_progress, completed, cancelled
|
||||
priority = db.Column(db.String(50), default='medium') # high, medium, low
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
assignee = db.relationship('User', backref='assigned_tasks')
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'project_id': self.project_id,
|
||||
'title': self.title,
|
||||
'description': self.description,
|
||||
'assignee_id': self.assignee_id,
|
||||
'assignee_name': self.assignee.username if self.assignee else None,
|
||||
'due_date': self.due_date.isoformat() if self.due_date else None,
|
||||
'status': self.status,
|
||||
'priority': self.priority,
|
||||
'created_at': self.created_at.isoformat(),
|
||||
'updated_at': self.updated_at.isoformat()
|
||||
}
|
||||
|
||||
class ResearchFile(db.Model):
|
||||
"""研究文件模型"""
|
||||
__tablename__ = 'research_files'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
project_id = db.Column(db.Integer, db.ForeignKey('research_projects.id'), nullable=False)
|
||||
name = db.Column(db.String(200), nullable=False)
|
||||
file_path = db.Column(db.String(500), nullable=False)
|
||||
file_size = db.Column(db.Integer)
|
||||
file_type = db.Column(db.String(50))
|
||||
uploader_id = db.Column(db.Integer, db.ForeignKey('users.id'))
|
||||
uploaded_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
uploader = db.relationship('User', backref='uploaded_research_files')
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'project_id': self.project_id,
|
||||
'name': self.name,
|
||||
'file_path': self.file_path,
|
||||
'file_size': self.file_size,
|
||||
'file_type': self.file_type,
|
||||
'uploader_id': self.uploader_id,
|
||||
'uploaded_at': self.uploaded_at.isoformat()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user