总结十
近期工作
这周的原定计划是完成任务等待和任务训练的单元测试,但是由于我们之前对于websocket没有进行深入了解出现了一些问题,导致我们最后只完成了任务等待的测试;同时我们主要到对于等待过程的退出发布者和参与者是不同的,于是乎我们的返回功能也需要进行修改,对于发布者来说,返回意味着任务的取消,所以需要删除任务,同时提醒其他用户任务已取消;对于参与者来说,返回意味着自己将不再参与这次任务训练,所以需要将原本的数据修改。
接下来我将先说一下websocket的一些遇到的问题以及补救措施,同时说一下最后任务等待的实时刷新改为手动刷新的原因。
1.Websocket的问题
websocket建立连接之后,后端主动退出,前端可以通过onclose函数进行捕获处理,但是前端主动退出,后端没有相应的函数进行捕获,这就导致了我们想通过websocket的前端断开连接进行返回的想法破灭。
下面是websocket的一些源码
def close(self, code=None):
"""
Closes the WebSocket from the server end
"""
if code is not None and code is not True:
super().send({"type": "websocket.close", "code": code})
else:
super().send({"type": "websocket.close"})
最初我们以为close方法和前端一样是处理对方断开连接的(当时没注意注释),但是通过实践发现不是,这个是服务器断开连接时调用。
def disconnect(self, code):
"""
Called when a WebSocket connection is closed.
"""
pass
disconnect方法和close方法的情况类似
2.任务等待的手动刷新实现
实时刷新需要让任务等待的websocket的receive方法进行死循环持续反馈最新信息,本来的想法:返回功能的实现是通过前端主动断开连接,websocket的close方法或者disconnect方法能够捕获被调用,控制死循环终止,后面发现这两个函数的用法不是我们说想的那样。
后面又尝试点击返回按钮再次发送数据提示返回,发现后端的websocket在陷入死循环的时候,其他的响应就失效了。
最后只能选择手动断开连接,下面是代码展示:
class WaitingConsumer(WebsocketConsumer):
def connect(self):
self.accept()
print('WaitingConsumer connect!')
def disconnect(self, code):
print('WaitingConsumer disconnect!')
def send(self, text_data=None, bytes_data=None, close=False):
return super().send(text_data, bytes_data, close)
def receive(self, text_data=None, bytes_data=None):
# 前端需要发送的数据:个人账号(方便统计,字段名称:account)、任务id(进行确认,字段名称:task_id)
returnValue = {
'judge': False,
'accounts': '',
'message_state': True,
'message': '',
'refresh': False,
'refreshMessage': '',
}
# TODO 获取前端的数据,更新当前任务的人数等待并进行确认
text_data_json = json.loads(text_data)
print(text_data_json)
# 获取json数据
account = text_data_json['account']
task_id = text_data_json['task_id']
label = text_data_json['label']
if label == 0:
# 等待
# 更新数据库(已有人数加一,账号统计字符串追加(原先:a, -> 后来增加了b变成:a,b,))
# 这里的','是英文字符
# 这个时候前端展示之前,后端将最后一个','去掉
mission = Mission.objects.get(mission_id=task_id)
current_user = mission.current_user + 1
user_accounts = mission.user_accounts + account + ','
print(current_user)
print(user_accounts)
Mission.objects.filter(mission_id=task_id).update(
current_user=current_user,
user_accounts=user_accounts
)
# 如果查询不到,说明任务已经取消
if mission is None:
returnValue['message_state'] = False
returnValue['message'] = '任务已取消'
self.send(text_data=json.dumps(returnValue))
elif label == 1:
# 刷新
returnValue['refresh'] = True
# 进行查询
mission = Mission.objects.get(mission_id=task_id)
# 如果查询不到,说明任务已经取消
if mission is None:
returnValue['message_state'] = False
returnValue['message'] = '任务已取消!'
self.send(text_data=json.dumps(returnValue))
current_user = mission.current_user
user_accounts = mission.user_accounts
need = mission.demand_user
returnValue['need'] = need
returnValue['have'] = current_user
if returnValue['refresh']:
returnValue['refreshMessage'] = '刷新成功'
if need <= current_user:
# 可以进行模型训练, 循环可以退出
accounts = user_accounts.rstrip(',')
returnValue['accounts'] = accounts
returnValue['judge'] = True
self.send(text_data=json.dumps(returnValue))
else:
# 反馈给前端账号信息
accounts = user_accounts.rstrip(',')
returnValue['accounts'] = accounts
self.send(text_data=json.dumps(returnValue))
主要是receive方法的实现。首先获取前端发送过来的数据:account、task_id(等同于mission_id)和label,account是为了更新数据库的参与人员名单,task_id是为了定位到具体任务,label是为了判断是更新数据还是刷新数据,剩下的就是对数据库的操作,最后判断一下需求人数和参与人数,如果足够就可以给前端提示可以开始训练。
3.返回功能的分解(针对发布者和参与者)
首先是url的路由设置,区分发布者和参与者。发布者取消任务,参与者停止等待退出任务。
# 参与者在任务等待过程中退出
path('quitWaiting', views.quitWaiting),
# 发起者在任务训练开始之前进行任务取消
path('cancelMission', views.cancelMission),
首先是参与者的代码:
@require_http_methods(["POST"])
def quitWaiting(request):
response = {'result': False,'message': ''}
print('正在退出循环等待...')
jsonData = json.loads(request.body)
task_id = jsonData['task_id']
account = jsonData['account']
try:
# 更改数据库的值,人数减一,字符串减少(需要先读取,再更新)
mission = Mission.objects.get(mission_id=task_id)
if mission.mission_state != 'waiting':
response['message'] = '训练已开始,请开始训练'
return JsonResponse(response)
# 人数更新
current_user = mission.current_user - 1
# 读取账号字符串,减去个人账号,更新数据库中账号字符串的值
temp = account + ','
user_accounts = mission.user_accounts.replace(temp, '')
Mission.objects.filter(mission_id=task_id).update(
current_user=current_user,
user_accounts=user_accounts
)
# 最后更改循环的条件,退出循环
print('退出成功')
# True表示退出成功
response['result'] = True
response['message'] = '返回成功'
except:
response['message'] = '返回失败'
return JsonResponse(response)
本质上就是和进行任务等待更新操作相反的操作,唯一值得注意的就是对于参与人员名单的修改,这里用的是python字符串的replace方法,mission.user_accounts.replace(temp, '')
接下来是发布者的取消任务(这个部分不是我写的,这里简单介绍一下即可)
# 取消任务,条件,需要任务id以及发起人账号,以及任务状态为未开始
# 发现为非发起人则返回仅任务发起人有取消权限
# 发现任务状态不对返回该任务状态下不可取消
# 取消后删除对应任务表项
@require_http_methods(["POST"])
def cancelMission(request):
jsonData = json.loads(request.body)
mission_id = jsonData['mission_id']
response = {}
try:
print(mission_id)
res = Mission.objects.get(mission_id=mission_id)
except:
response = {"results": "该任务不存在"}
return JsonResponse(response)
else:
if res.mission_state != 'waiting':
response["results"] = "任务仅可在waiting状态下取消"
return JsonResponse(response)
else:
# 删除对应项
Mission.objects.get(mission_id=mission_id).delete()
response["results"] = "任务取消成功"
return JsonResponse(response)
先判断任务是否存在,再判断任务是否处于waiting状态,最后即可删除。这里解释一下有关任务状态的设定,任务状态主要有三种,分别为waiting、training和finish,这里判断是否处于waiting状态的依据就在于,任务是否已经开始训练,如果任务开始训练了,这时候删除会对其他人造成影响,故不可删除。
下步计划
1.完成任务训练的单元测试
2.进行集成测试和系统测试
3.部署进行发布前的验收测试