结对作业第二次——编程实现

222200333曾雅婷 2024-09-30 23:51:04
这个作业属于哪个课程24秋-软件工程&实践-W班
这个作业要求在哪里结对第二次作业——编程实现
结对学号222200333, 222200429
这个作业的目标使用web技术实现原型的功能
其他参考文献构建之法

目录

  • 一、git仓库链接和代码规范链接
  • 二、PSP表格
  • 三、成品展示
  • 3.1 基础功能
  • 3.1.1首页
  • 3.1.2 奖牌榜
  • 3.1.3 每日赛程
  • 3.1.4 对阵图
  • 3.2 附加功能
  • 3.2.1 详细赛况
  • 3.2.2 个人奖牌榜
  • 3.2.3 了解更多
  • 四、结对讨论过程描述
  • 五、设计实现过程
  • 5.1 技术路线设计
  • 六、代码说明
  • 6.1 导航栏实现
  • 6.2 奖牌榜
  • 6.3 每日赛程
  • 6.4 对阵表
  • 6.5 详细赛况
  • 七、心路历程和收获
  • 八、评价
  • 九、其他

一、git仓库链接和代码规范链接

PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划2020
• Estimate估计这个任务需要多少时间2020
Development开发11901350
• Analysis需求分析 (包括学习新技术)6030
• Design Spec生成设计文档6050
• Design Review设计复审3050
• Coding Standard代码规范 (为目前的开发制定合适的规范)2020
• Design具体设计300340
• Coding具体编码600660
• Code Review代码复审60180
• Test测试(自我测试,修改代码,提交修改)6020
Reporting报告9090
• Test Repor测试报告3030
• Size Measurement计算工作量3030
• Postmortem & Process Improvement Plan事后总结, 并提出过程改进计划3030
合计13001460

三、成品展示

3.1 基础功能

3.1.1首页

  • 首页导航栏
  • 展示奥运咨询和风采图片
  • 奥运奖牌榜、每日赛程、详细赛况的快捷入口

    在这里插入图片描述


    在这里插入图片描述


    在这里插入图片描述

3.1.2 奖牌榜

  • 展示所有参赛国家的获奖情况,按照获取的金、银、铜牌数进行排名

    在这里插入图片描述

3.1.3 每日赛程

  • 页面展示每日赛程
  • 可以通过日期选择器切换日期
  • 可以通过🥇/🥈/🥉/√ 图片快速找到每场比赛获胜者

    在这里插入图片描述

3.1.4 对阵图

在这里插入图片描述

3.2 附加功能

3.2.1 详细赛况

  • 提供小组赛、晋级赛、奖牌赛标签选择,可切换滚动面板展示内容
  • 选择对应的赛程,可见详细赛况,包括:出赛名单、比赛详情
  • 在这里插入图片描述


    在这里插入图片描述

3.2.2 个人奖牌榜

  • 展示所有参赛选手的获奖情况(不包含团体赛),按照获取的金、银、铜牌数进行排名

    在这里插入图片描述

3.2.3 了解更多

  • 展示奥运咨询和风采图片

在这里插入图片描述

四、结对讨论过程描述

222200429 谢雨欣 负责前端
222200333 曾雅婷 负责后端
最开始我们本来打算按照纯前端做,后面我与我的搭档探讨了一下,我们觉得Json数据有点乱,不方便提取,正好我的搭档习惯使用python写后端,便于处理数据。所以我们最后转用前后端实现。在实现过程中,由于我接口这部分比较不熟悉,从纯前端改前后端对接的时候改了很久,我的搭档也很耐心的帮了我很多。
以下是我们的一部分聊天截图

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述

五、设计实现过程

  • 功能结构图

在这里插入图片描述

5.1 技术路线设计

前端:vue3
后端:python
前端接口:Ajax
后端接口:Flask

六、代码说明

6.1 导航栏实现

前端:在components里面创建了一个导航栏组件NavBar.vue,设置好导航栏的样式,尽可能还原原型设计,并对我的导航栏添加了动态效果,添加超链接实现跳转页面功能,在App.vue中引用它,使得导航栏位于每个页面的顶端。

在这里插入图片描述

6.2 奖牌榜

前端:

  • 由于后面改成前后端对接,时间不够,奖牌榜我采用的是最简单的表格样式。国家国旗的显示我们是通过从后端接收国家名称缩写,访问网址引用图片。个人金牌榜的实现方式和国家奖牌榜大同小异,只是接收的数据不同。

    在这里插入图片描述


    在这里插入图片描述


    后端:
  • 数据处理&通过接口响应前端请求
  • 传递"noc"值便于放置国旗
  • 关于个人奖牌榜的数据处理

(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)
]

6.3 每日赛程

前端:

  • 使用 <input type="date"> 创建一个日期选择器,允许用户选择日期。使用<div>标签和v-for指令循环展示每场比赛的信息,从起始日期开始,生成19个连续的日期。在脚本部分,我定义了一些方法来处理用户交互和数据操作。

  • getScorePrefix(team):根据队伍的奖牌类型返回相应的图标路径。

  • handleDateChange():当用户选择日期时,这个方法会被调用,用于从后端获取比赛数据。

    在这里插入图片描述


    后端:

  • 数据处理&简单接口传值。

    6.4 对阵表

    前端:

  • 对阵表的连线其实我一开始不知道怎么实现,我问了几个同学,试了几个方法,最后在figma上实现连线导出图片,并调整图片位置渲染效果。

    在这里插入图片描述

    6.5 详细赛况

    前端:

  • 我定义了一个selectedGroupMatches属性来获取当前选中组别的比赛信息和一个totalPages属性来计算总页数。

  • 我定义了一个fetchMatchesData方法来获取比赛数据,使用axios向后端发送请求并处理响应。

  • 我定义了一个方法来处理组别的选择,更新selectedGroup和currentPage,并重新获取数据。

在这里插入图片描述


后端:

  • 数据处理
    (1)html页面元素定位(非常需要耐心细心分析的一步)

出赛名单数据处理:
最开始定位时发现会有许多其他相同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很适合写这个简单的后端。在前后端对接的过程中,增进了对网络通信的理解,也认识到了沟通协作的重要性。不过这次任务还是完成得太极限了,还是应该考虑清楚自己的能力范围,量力而行即可。

八、评价

谢雨欣:选择纯前端转前后端对接的时候我很担心,我的搭档鼓励我并安慰我,在对接过程中也帮了我很多。我的搭档积极上进,应变能力强,及时分享进度且基础很好,这次的结对作业虽然很辛苦但也收获了很多。
曾雅婷:我的队友非常可靠,非常值得信任。她能够提前的规划和着手工作并且效率很好,是非常值得学习的。在遇到困难的时候也能及时告知,共同解决问题,保持项目的健康进度。此次作业我们挑战了很多难点,并最终有惊无险地完成了任务。

九、其他

  • 数据爬取行为仅用于课程教学
...全文
144 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

239

社区成员

发帖
与我相关
我的任务
社区管理员
  • FZU_SE_teacherW
  • 助教赖晋松
  • D's Honey
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧