schema.sql
与models.py
中的class Project
新建项目(create.html)
的POST接口,跳转,数据存储所有项目(all.html)
的前端html
与css
设计
header
右侧<div class=data></div>
中的所有<li></li>
数据排为一行record
按钮与/create_record
路由
record(inactive) -- stop(active)
create_record
activate
属性后的数据库初始化与原始数据的兼容性迁移
flask db upgrade
, flask db init
SQL命令所有项目(all.html)
页面的projects
数据根据project.all_time
所转换的时间长度的值进行降序排列status
按照状态分区展示:纵列/横排(看板)
In Planning
In Process
Pause
Completed
Abandon
more
按钮与delete
,rename
,edit(status,note)
profile
close_account
注销账号,删除账号密码及其所有相关数据edit
修改账号密码,用户名传入的start_time,end_time为'2023-09-04 10:05:37.000000', '2023-09-04 10:05:39.000000',而非'2023-09-04 10:05:37', '2023-09-04 10:05:39',哪一步出了问题?如何解决?给出具体代码
已知
def parse_datetime(datetime_str):
# 解析日期时间字符串并返回 datetime 对象
return datetime.strptime(datetime_str, '%Y-%m-%d %H:%M:%S')
record = Record(
start_time=parse_datetime(start_time),
end_time=parse_datetime(end_time),
project_id=project_id
)
修改
def __init__(self, start_time, end_time, project_id):
self.start_time = start_time # 初始化记录的开始时间
self.end_time = end_time # 初始化记录的结束时间
# 计算并设置记录的时长,timedelta类型转换为字符串
self.duration = str(end_time - start_time) #str(datetime.timedelta)
self.project_id = project_id # 初始化记录所属项目ID
#self.project.update_time_stats()
中的
self.duration = str(end_time - start_time) #str(datetime.timedelta)
是duration成为 HH:MM::SS格式的String
···
<div class="container">
{% for project in projects %}
<div class="project-card">
<ul>
<!--项目排名序号-->
<li class="project-rank">{{ loop.index }}</li>
<!--计时按钮,status='Pause'时文本显示为'开始计时',status='In Process'文本显示为停止计时-->
<li class="project-timer">
<button id="timer-btn" project-id="{{ project.id }}">Record</button>
</li>
<!--项目名称-->
<li class="project-name">{{ project.name }}</li>
<!--项目状态-->
<li class="project-status">{{ project.status }}</li>
<!--项目总计时间-->
<li class="project-total-time">{{ project.all_time }}</li>
<!--项目更多按钮-->
<li class="project-more">
<button class="more" onclick="">更多</button>
</li>
</ul>
</div>
{% endfor %}
</div>
从以上代码可以发现,这里有若干个project组成的模块,每个模块里都有一个id="timer-btn"
的按钮,其有一个参数为id="timer-btn"
,因project而异。
这是project中数据存储的格式 all_time = db.Column(db.String)
daily_time = db.Column(JSONEncodedDict, default={})
weekly_time = db.Column(JSONEncodedDict, default={})
monthly_time = db.Column(JSONEncodedDict, default={})
yearly_time = db.Column(JSONEncodedDict, default={})
他们都是基于文本的格式,而非时间。
当我进行update_time_stats(self,record)时,传入的record中包含的数据有:id = db.Column(db.Integer, primary_key=True,autoincrement=True) # 记录ID,整数类型,主键
project_id = db.Column(db.Integer, db.ForeignKey('projects.id'), nullable=False) # 项目ID,整数类型,外键,不能为空
start时间 = db.Column(db.DateTime, nullable=False) # 记录开始时间,日期时间类型,不能为空
end_time = db.Column(db.DateTime, nullable=False) # 记录结束时间,日期时间类型,不能为空
duration = db.Column(db.String, nullable=False, default='00:00:00') # 记录时长,字符串类型,不能为空
其中duration为文本类型数据,我需要一个函数来进行对文本存储的时长的计算,提取record中的duration为某一个标准类型,然后update_time_stats(self,record)调用该格式转换函数进行计算日期,更新project的数据参数。update_time_stats(self,record):定义如下,可进行修改,给出所有相关代码:
def update_time_stats(self,record):
date = record.start_time.date()
day = date.strftime("%Y-%m-%d")
week = date.strftime("%Y-W%W")
month = date.strftime("%Y-%m")
year = date.strftime("%Y")
if date.isoformat() in self.daily_time:
self.daily_time[day] += record.duration
else:
self.daily_time[day] = record.duration
if week in self.weekly_time:
self.weekly_time[week] += record.duration
else:
self.weekly_time[week] = record.duration
if month in self.monthly_time:
self.monthly_time[month] += record.duration
else:
self.monthly_time[month] = record.duration
if year in self.yearly_time:
self.yearly_time[year] += record.duration
else:
self.yearly_time[year] = record.duration
self.calculate_time()
db.session.commit()
在all.html
展示所有项目数据。以若干个卡片列表的形式。每一个卡片从左到右依次有开始计时
/停止计时
按钮,默认为开始计时
,状态为暂停
,按下按钮后状态切换为进行中
,同时按钮文本切换为停止计时
,记录下此时的DateTime作为本次Record
的start_time
,再次点击按钮(此时钮文本为停止计时
),状态从进行中
切换为暂停
,按钮文本切换回开始计时
,记录下此时的DateTime作为本次Record
的end_time
,完成一次Record
,存入数据库中,计算duration=end_time=start_time. 在all_time上+=本次record的duration,在daily_time查询当天日期对应的JSON{date:YYYY-MM-DD,time:HH:MM:SS},并使得time的值+=duration。若未查询到,则新建一个JSON数据,time默认初始化为0后+=duration。数据实时更新到all.html上,weekly,monthly,yearly以此类推。
默认文本显示为record
,按钮状态为inactive
,按下按钮后状态切换为active
,同时按钮文本切换为stop
,记录下此时的DateTime作为本次Record
的start_time
,再次点击按钮(此时钮文本为stop
),状态从active
切换为inactive
,按钮文本切换回record
,记录下此时的DateTime作为本次Record
的end_time
,完成一次record=Record(start_time,end_time)
,commit到数据库中,调用project.update_time_stats(record)
在all_time上+=本次record的duration,在daily_time查询当天日期对应的JSON{date:YYYY-MM-DD,time:HH:MM:SS},并使得time的值+=duration。若未查询到,则新建一个JSON数据,time默认初始化为0后+=duration。数据实时更新到all.html上,weekly,monthly,yearly以此类推。
编辑按钮的html和路由,给出具体完整的代码
active/inactive
的改变而改变stript.js
schema.sql
models.py
views.py
# /create POST
current_user
request{
name,
status,
}
project(
id = ,#生成
name = request.form['name'],
status = request.form['status'],
user_id = current_user.id,
)
###
sqlalchemy.exc.StatementError: (builtins.TypeError) SQLite DateTime type only accepts Python datetime and date objects as input.
[SQL: INSERT INTO projects (name, status, user_id, all_time, daily_time, weekly_time, monthly_time, yearly_time) VALUES (?, ?, ?, ?, ?, ?, ?, ?)]
[parameters: [{'status': 'In Planning', 'user_id': 1, 'name': '1', 'monthly_time': None, 'daily_time': None, 'yearly_time': None, 'weekly_time': None}]]
###
#创建一条record
record = Record(
project_id=project.id,
start_time=datetime.now(),#第一次按钮,开始计时
end_time=datetime.now() #第二次按钮,结束计时
)
record.duration=0
db.session.add(record)
db.session.commit()
project.update_time_stats(record)
针对project类,其schema.sql如下:
-- 创建 projects 表
CREATE TABLE IF NOT EXISTS Project (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
name TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'In Planning', // 项目状态 InPlanning, InProgress, Completed, Abandoned, Paused
all_time DATETIME NOT NULL DEFAULT '00:00:00',
daily_time JSON, -- 日时间,TEXT类型存储JSON
weekly_time JSON,
monthly_time JSON,
yearly_time JSON,
FOREIGN KEY (user_id) REFERENCES User (id)
);
其中'monthly_time': None, 'daily_time': None, 'yearly_time': None, 'weekly_time': None}
默认为None,非JSON格式。当一个project被创建出来而还没有records
的时候,如何将这些JSON数据指定一个默认值?
sqlalchemy.exc.StatementError: (builtins.TypeError) SQLite DateTime type only accepts Python datetime and date objects as input.
[SQL: INSERT INTO projects (name, status, user_id, all_time, daily_time, weekly_time, monthly_time, yearly_time) VALUES (?, ?, ?, ?, ?, ?, ?, ?)]
[parameters: [{'name': 'a', 'status': 'In Planning', 'user_id': 1}]]
@bp.route('/project/<int:id>/delete', methods=['DELETE'])
def delete(id):
project = Project.query.get(id)
db.session.delete(project)
db.session.commit()
return jsonify(status='success')
@bp.route('/project/<int:id>/edit', methods=['GET','POST'])
def edit(id):
project = Project.query.get(id)
if request.method == 'POST':
project.name = request.form['name']
project.status = request.form['status']
db.session.commit()
return redirect(url_for('main.all'))
return render_template('edit.html', project=project)
@bp.route('/project/<int:id>/start', methods=['POST'])
def start(id):
project = Project.query.get(id)
record = Record(project_id=project.id)
db.session.add(record)
db.session.commit()
return jsonify(status='success')
@bp.route('/project/<int:id>/stop', methods=['PUT'])
def stop(id):
record = Record.query.filter_by(project_id=id).order_by(Record.start_time.desc()).first()
record.end_time = datetime.now()
db.session.commit()
return jsonify(status='success')
@bp.route('/project/<int:id>/delete_record', methods=['DELETE'])
def delete_record(id):
record = Record.query.get(id)
db.session.delete(record)
db.session.commit()
return jsonify(status='success')
@bp.route('/project/<int:id>/update_record', methods=['PUT'])
def update_record(id):
record = Record.query.get(id)
record.start_time = datetime.strptime(request.form['start_time'],'%Y-%m-%d %H:%M:%S')
record.end_time = datetime.strptime(request.form['end_time'],'%Y-%m-%d %H:%M:%S')
record.duration = record.end_time - record.start_time
db.session.commit()
return jsonify(status='success')
@bp.route('/project/<int:id>/records')
def get_records(id):
records = Record.query.filter_by(project_id=id).order_by(Record.start_time.desc()).all()
return jsonify(records=[record.serialize() for record in records])
@bp.route('/project/<int:id>/records/<int:page>')
def get_records_by_page(id,page):
records = Record.query.filter_by(project_id=id).order_by(Record.start_time.desc()).paginate(page=page, per_page=10)
return jsonify(records=[record.serialize() for record in records.items])
@bp.route('/project/<int:id>/records/<int:page>/<int:per_page>')
def get_records_by_page_and_per_page(id,page,per_page):
records = Record.query.filter_by(project_id=id).order_by(Record.start_time.desc()).paginate(page=page, per_page=per_page)
return jsonify(records=[record.serialize() for record in records.items])
@bp.route('/project/<int:id>/records/<string:start_time>/<string:end_time>')
def get_records_by_time(id,start_time,end_time):
records = Record.query.filter_by(project_id=id).filter(Record.start_time.between(start_time,end_time)).order_by(Record.start_time.desc()).all()
return jsonify(records=[record.serialize() for record in records])
@bp.route('/project/<int:id>/records/<string:start_time>/<string:end_time>/<int:page>')
def get_records_by_time_and_page(id,start_time,end_time,page):
records = Record.query.filter_by(project_id=id).filter(Record.start_time.between(start_time,end_time)).order_by(Record.start_time.desc()).paginate(page=page, per_page=10)
return jsonify(records=[record.serialize() for record in records.items])
@bp.route('/project/<int:id>/records/<string:start_time>/<string:end_time>/<int:page>/<int:per_page>')
def get_records_by_time_and_page_and_per_page(id,start_time,end_time,page,per_page):
records = Record.query.filter_by(project_id=id).filter(Record.start_time.between(start_time,end_time)).order_by(Record.start_time.desc()).paginate(page=page, per_page=per_page)
return jsonify(records=[record.serialize() for record in records.items])
# 项目详情
@bp.route('/project/<int:id>')
def project(id):
project = Project.query.get(id)
return render_template('views/project.html', project=project)
# 项目详情
@bp.route('/project/<int:id>/status', methods=['GET'])
def get_project_status(id):
project = Project.query.get(id)
return jsonify(status=project.status)
<div class="container">
{% for project in projects %}
<ul>
<li></li><!--添加一个排序的序号-->
<li><button class="actions" onclick="toggleStatus({{project.id}})">按钮</button></li>
<!--`开始计时`/`停止计时`按钮,默认为`开始计时`,状态为`暂停`,按下按钮后状态切换为`进行中`,同时按钮文本切换为`停止计时`,记录下此时的DateTime作为本次`Record`的`start_time`,再次点击按钮(此时钮文本为`停止计时`),状态从`进行中`切换为`暂停`,按钮文本切换回`开始计时`,记录下此时的DateTime作为本次`Record`的`end_time`,完成一次`Record`,存入数据库中,计算duration=end_time=start_time. 在all_time上+=本次record的duration,在daily_time查询当天日期对应的JSON{date:YYYY-MM-DD,time:HH:MM:SS},并使得time的值+=duration。若未查询到,则新建一个JSON数据,time默认初始化为0后+=duration。数据实时更新到all.html上,weekly,monthly,yearly以此类推。-->
<li class="details">{{ project.name }}</li>
<li class="details">{{ project.status }}</li>
<li class="details">{{ project.total_time }}</li>
<li><button class="more" onclick="">more</button></li> <!--more按钮,点击可选择delete,rename等按钮-->
</ul>
{% endfor %}
</div>
Array.from(timerBtns).forEach(function(btn) {
btn.addEventListener('click', function() {
let projectId = btn.dataset.projectId;
if (projectId) {
if (btn.classList.contains('active')) {
// Stop timer
btn.classList.remove('active');
btn.innerText = 'Record';
let endTime = new Date();
let record = {
project_id: projectId,
start_time: formatDateTime(startTime),
end_time: formatDateTime(endTime)
};
createRecord(record);
startTime = null;
} else {
// Start timer
startTime = new Date();
btn.classList.add('active');
btn.innerText = 'Stop';
}
}
});
});