110
社区成员
这个作业属于哪个课程 | 24秋-软件工程&实践-W班 |
---|---|
这个作业要求在哪里 | 结对第二次作业——编程实现 |
结对学号 | 222200333, 222200429 |
这个作业的目标 | 使用web技术实现原型的功能 |
其他参考文献 | 构建之法 |
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 20 |
• Estimate | 估计这个任务需要多少时间 | 20 | 20 |
Development | 开发 | 1190 | 1350 |
• Analysis | 需求分析 (包括学习新技术) | 60 | 30 |
• Design Spec | 生成设计文档 | 60 | 50 |
• Design Review | 设计复审 | 30 | 50 |
• Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 20 | 20 |
• Design | 具体设计 | 300 | 340 |
• Coding | 具体编码 | 600 | 660 |
• Code Review | 代码复审 | 60 | 180 |
• Test | 测试(自我测试,修改代码,提交修改) | 60 | 20 |
Reporting | 报告 | 90 | 90 |
• Test Repor | 测试报告 | 30 | 30 |
• Size Measurement | 计算工作量 | 30 | 30 |
• Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 1300 | 1460 |
222200429 谢雨欣 负责前端
222200333 曾雅婷 负责后端
最开始我们本来打算按照纯前端做,后面我与我的搭档探讨了一下,我们觉得Json数据有点乱,不方便提取,正好我的搭档习惯使用python写后端,便于处理数据。所以我们最后转用前后端实现。在实现过程中,由于我接口这部分比较不熟悉,从纯前端改前后端对接的时候改了很久,我的搭档也很耐心的帮了我很多。
以下是我们的一部分聊天截图
前端:vue3
后端:python
前端接口:Ajax
后端接口:Flask
前端:在components里面创建了一个导航栏组件NavBar.vue,设置好导航栏的样式,尽可能还原原型设计,并对我的导航栏添加了动态效果,添加超链接实现跳转页面功能,在App.vue中引用它,使得导航栏位于每个页面的顶端。
前端:
(1)在json文件中找到所有有关数据并进行统计处理
for item in data['props']['pageProps']['initialMedals']['medalStandings']['medalsTable']:
for discipline in item['disciplines']:
for winner in discipline['medalWinners']:
if winner['competitorType']=="A":
competitorName = winner['competitorDisplayName']
countryName = item['longDescription']
noc=item['organisation'],
medalType = winner['medalType']
if medalType == 'ME_GOLD':
medal_stats[competitorName]['gold'] += 1
elif medalType == 'ME_SILVER':
medal_stats[competitorName]['silver'] += 1
elif medalType == 'ME_BRONZE':
medal_stats[competitorName]['bronze'] += 1
medal_stats[competitorName]['country'] = countryName
medal_stats[competitorName]['noc'] = noc
for name in medal_stats:
stats = medal_stats[name]
stats['total'] = stats['gold'] + stats['silver'] + stats['bronze']
(2)根据统计数据对选手进行排名并赋值排名
medal_info_sorted = sorted(
[
(name, stats['country'], stats['noc'][0], stats['gold'], stats['silver'], stats['bronze'], stats['total'])
for name, stats in medal_stats.items()
],
key=lambda x: (-x[6],-x[3], -x[4], -x[5])
)
(3)json数据结构
playerMedals=[
{
"rank":i+1,
"competitorName": entry[0],
"countryName": entry[1],
"noc":entry[2],
"gold": entry[3],
"silver": entry[4],
"bronze": entry[5],
"total": entry[6]
}
for i,entry in enumerate(medal_info_sorted)
]
前端:
使用 <input type="date">
创建一个日期选择器,允许用户选择日期。使用<div>
标签和v-for指令循环展示每场比赛的信息,从起始日期开始,生成19个连续的日期。在脚本部分,我定义了一些方法来处理用户交互和数据操作。
getScorePrefix(team):根据队伍的奖牌类型返回相应的图标路径。
handleDateChange():当用户选择日期时,这个方法会被调用,用于从后端获取比赛数据。
数据处理&简单接口传值。
前端:
对阵表的连线其实我一开始不知道怎么实现,我问了几个同学,试了几个方法,最后在figma上实现连线导出图片,并调整图片位置渲染效果。
前端:
我定义了一个selectedGroupMatches属性来获取当前选中组别的比赛信息和一个totalPages属性来计算总页数。
我定义了一个fetchMatchesData方法来获取比赛数据,使用axios向后端发送请求并处理响应。
我定义了一个方法来处理组别的选择,更新selectedGroup和currentPage,并重新获取数据。
出赛名单数据处理:
最开始定位时发现会有许多其他相同class值的无关数据被读取,需要通过观察结构、打印调试等方式确认所需元素的确切位置。还需要对读取到的信息进行处理,格式可能包含换行符。
for i, table in enumerate(tables):
tbody = table.find('tbody', role='rowgroup')
if tbody:
for tr in tbody.find_all('tr'):
number = tr.find('td', role='cell', class_='text-end')
cells = tr.find_all('td', role='cell', class_='text-start')
if len(cells) == 2:
comp_container = cells[0].find('a', class_='competitor-container')
name = comp_container.find('span', class_='competitor-long-name d-none d-sm-inline-block text-truncate').text.strip() if comp_container else ""
name = clean_string(name) if name else ""
position = cells[1].text.strip()
player = Player(number, name, position)
if i == 0:
starters_a.append(player.to_dict())
elif i == 1:
substitutes_a.append(player.to_dict())
elif i == 3:
starters_b.append(player.to_dict())
elif i == 5:
substitutes_b.append(player.to_dict())
for i, row in enumerate(rows):
if i == 1:
countryName_a = row.find('span', class_='competitor-long-name d-none d-sm-inline-block text-truncate').text.strip()
if i == 2:
coach_a = row.find('span', class_='competitor-long-name d-none d-sm-inline-block text-truncate')
coach_a = clean_string(coach_a.text) if coach_a else ""
if i == 6:
countryName_b = row.find('span', class_='competitor-long-name d-none d-sm-inline-block text-truncate')
countryName_b = clean_string(countryName_b.text) if countryName_b else ""
if i == 7:
coach_b = row.find('span', class_='competitor-long-name')
coach_b = clean_string(coach_b.text) if coach_b else ""
详细赛况数据处理:
需要逐步找到页面元素并处理。
soup=BeautifulSoup(content,'html.parser')
accordion=soup.find('div',class_='accordion')
H1=accordion.find('div',id='mirs-accordion-item-H1')
H2=accordion.find('div',id='mirs-accordion-item-H2')
h1Body=H1.find('div',class_='accordion-body')
h2Body=H2.find('div',class_='accordion-body')
h1Rows=h1Body.find_all('div',class_='row')
h2Rows=h2Body.find_all('div',class_='row')
# 处理First Half
for i,row in enumerate(h1Rows):
time=row.find('div',class_='box-center text-center flex-column').find('div')
time = clean_string(time.text) if time else ""
infor=row.find('div',class_='box flex-column flex-nowrap')
infor=clean_string(infor.text) if infor else ""
gameInfor = GameInfor(time,infor)
h1GameINfor.append(gameInfor.to_dict())
# 处理Second Half
for i,row in enumerate(h2Rows):
time=row.find('div',class_='box-center text-center flex-column').find('div')
time = clean_string(time.text) if time else ""
infor=row.find('div',class_='box flex-column flex-nowrap')
infor=clean_string(infor.text) if infor else ""
gameInfor = GameInfor(time,infor)
h2GameINfor.append(gameInfor.to_dict())
(2)json数据结构
出赛名单:
data = {
"id": id, # id 为对应的比赛分组和比赛编号 例如 1A 3C
"country_a": {
"countryName": countryName_a,
"coach": coach_a,
"starters": starters_a,
"substitutes": substitutes_a
},
"country_b": {
"countryName": countryName_b,
"coach": coach_b,
"starters": starters_b,
"substitutes": substitutes_b
}
}
详细赛况:
data ={
"id":id,
"H1GameInfors":h1GameINfor,
"H2GameInfors":h2GameINfor
}
(3)批量处理数据文件
html_files = glob.glob('rawinfor/*F.html')
接收两个参数,组别、比赛编号,根据组别查找数据文件,根据比赛编号和组别合成的值定位所需数据。
# 出赛名单
@app.route('/entryList',methods=['GET'])
def entryList():
group=request.args.get('group')
id=request.args.get('id')
id_value=f"{id}{group}"
print(f"id_value: {id_value}")
file_name=f"{group}s.json"
file_path=os.path.join('data/processinfor',file_name)
if os.path.exists(file_path):
with open(file_path,'r',encoding='utf-8') as f:
data=json.load(f)
for item in data:
if item['id']==id_value:
print("entrylist sucess")
return (item),200
return jsonify({"error":"id not found"}),404
else :
return jsonify({"error":"File not found"}),404
谢雨欣:
心路历程:我的项目经验较少,且给的时间并不多,我在原型设计结束不久就开始着手实现原型,我担心写的太慢会拖搭档的后腿。我之前写过项目的前端,但接口的部分我还不熟悉,我请教了团队中前端的负责人和我的搭档。在完成项目的过程中我们也遇到了很多问题,我们一点点的修改,完善。我们很用心在完成这次作业。
曾雅婷:
心路历程:这次编程作业熟悉了网站信息爬取,利用python进行、数据处理、写接口,以及与前后端的接口对接。网站爬取还是无法利用脚本,只能通过找包含内容的页面元素手动爬取,并编写脚本解析html文件获得数据。python很适合写这个简单的后端。在前后端对接的过程中,增进了对网络通信的理解,也认识到了沟通协作的重要性。不过这次任务还是完成得太极限了,还是应该考虑清楚自己的能力范围,量力而行即可。
谢雨欣:选择纯前端转前后端对接的时候我很担心,我的搭档鼓励我并安慰我,在对接过程中也帮了我很多。我的搭档积极上进,应变能力强,及时分享进度且基础很好,这次的结对作业虽然很辛苦但也收获了很多。
曾雅婷:我的队友非常可靠,非常值得信任。她能够提前的规划和着手工作并且效率很好,是非常值得学习的。在遇到困难的时候也能及时告知,共同解决问题,保持项目的健康进度。此次作业我们挑战了很多难点,并最终有惊无险地完成了任务。