结对作业二

作业信息

结对作业二

Q A
这个作业属于哪个课程 软件工程
这个作业要求在哪里 作业要求
结对学号 221801421,221801404
作业要求 实现论文查询和热词统计

Git仓库链接和代码规范链接

Github仓库

PSP表格

221801404

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 10 10
• Estimate • 估计这个任务需要多少时间 10 10
Development 开发 1480 1520
• Analysis • 需求分析 (包括学习新技术) 600 620
• Design Spec • 生成设计文档 50 40
• Design Review • 设计复审 120 100
• Coding Standard • 代码规范 (为目前的开发制定合适的规范) 40 30
• Design • 具体设计 120 110
• Coding • 具体编码 460 400
• Code Review • 代码复审 30 20
• Test • 测试(自我测试,修改代码,提交修改) 60 200
Reporting 报告 130 85
• Test Report • 测试报告 80 60
• Size Measurement • 计算工作量 10 15
• Postmortem & Process Improvement Plan • 事后总结, 并提出过程改进计划 40 10
合计 1740 1615

221801421

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 10 10
• Estimate • 估计这个任务需要多少时间 10 10
Development 开发 3300 2910
• Analysis • 需求分析 (包括学习新技术) 800 620
• Design Spec • 生成设计文档 60 40
• Design Review • 设计复审 140 100
• Coding Standard • 代码规范 (为目前的开发制定合适的规范) 40 30
• Design • 具体设计 50 40
• Coding • 具体编码 2000 1900
• Code Review • 代码复审 30 20
• Test • 测试(自我测试,修改代码,提交修改) 180 160
Reporting 报告 170 165
• Test Report • 测试报告 120 120
• Size Measurement • 计算工作量 10 15
• Postmortem & Process Improvement Plan • 事后总结, 并提出过程改进计划 40 30
合计 3480 3085

成品展示

以下是展示的Gif图,支持关键字和论文标题查询,有删除功能,并对热词进行了统计

结对讨论

在讨论的过程中我们重点是在数据的传输格式,因为我(221801404没有这么项目经验,再加上是第一次用flask框架且之前没有学习过python,所以在对数据格式的调整上花了挺多时间的。


设计实现过程

前端:

整体上采用Vue框架+ElementUI构建项目的基本框架,使用vue-router进行路由跳转,axios实现与后端的交互,echart来生成动态图表。

具体设计实现时先将头部栏和左侧导航栏写入主页,再在主页main区域放一个router-view,从而实现头部栏和导航栏的复用。剩余页面分为论文列表页、论文详情页和热词统计页,这些内容作为组件嵌入到main区域,会随着路由的跳转而在main区域展示相应的组件。

论文列表页:使用一个el-table展示论文的大致信息,上方搜索框可以让你搜索你所感兴趣的信息。

论文详情页:点击表格的一行可以跳转到该论文的详情页,这里展示的是论文的所有信息。右上角有个删除按钮,可对已爬取的论文进行删除。

热词统计页:热词统计页中有三大顶会的热词走势折线图,鼠标置于某年上可以浮现出当年的最热词。最右边还有个最热关键词top10,点击关键词即可跳转到相应关键词的查询。

后端:

后端部分采用flask框架来完成。本意是想通过模型类来对数据库进行操作,但由于环境配置一直报错,版本不兼容等等一系列问题导致模型类的包没有成功导入,所以后端部分的数据库操作都是用sql语句进行操作的。因为没有定义模型类,所以代码显得有一些冗余。
我定义了是三个获取某一会议的热词函数,输出此会议的热词及次数,最后再一个接口函数里将是哪个会议的额数据进行整合,一起发送到前端。另外删除操作和查询操作各一个函数,按指定格式输出。

功能结构图

代码说明

前端部分

考虑到页面结构其实大同小异,最难的其实是对陌生控件的使用,所以这里比起逐一说明各页面,更倾向于对各主要控件的使用。

前端导入依赖:

  "dependencies": {
    "axios": "^0.21.1",
    "element-ui": "^2.15.1",
    "vue": "^2.5.2",
    "vue-router": "^3.0.1"
  },

"devDependencies": {
    //此处省略其他
    "echarts": "^5.0.2",
}

路由配置:

history模式可以将url中的/#/去掉
剩下就是为每个组件(页面)添加自己的路径

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/',
      name:'ShowList',
      component: ShowList
    },
    {
      path:'/PaperDetail',
      name:'PaperDetail',
      component: PaperDetail
    },
    {
      path:'/BuzzWord',
      name:'BuzzWord',
      component:BuzzWord
    }
  ]
})

主页部分:实现头部栏和导航栏的复用

使用的是elementUI的导航栏,为导航栏的每一项设置默认路由,点击即可跳转到相应界面。这里的想法是进入网站默认是论文列表页,所以路由设置如下,'/'是论文列表ShowList,'/BuzzWord'是热词统计。

html部分:

<template>
<div id="app">
  <el-header>
      欢迎来到论文爬取网
  </el-header>
  <div id="wrap">
    <div id="aside">
      <el-menu :default-active="$route.path" router class="el-menu-vertical-demo">
        <el-menu-item index="/">
          <i class="el-icon-document"></i>
          <span slot="title">论文查询</span>
        </el-menu-item>
        <el-menu-item index="/BuzzWord">
          <i class="el-icon-s-flag"></i>
          <span slot="title">热词统计</span>
        </el-menu-item>
      </el-menu>
    </div>
    <router-view></router-view>
  </div>
</div>
</template>

论文列表页的论文展示表格 以及 页面之间的数据传输:

el-table的data属性需要一个对象数组,

@row-click是给表格的行设置的点击事件,

:show-overflow-tooltip='true'表示太长的行会在末尾用三个点省略

回调函数showDetail的三个参数中,row表示点击的行所对应的data中的对象。

由于需要实现带数据的页面跳转,所以我这里使用的是query。
其实这里可以用两种方式,

一是像我这里用path+query,

还有一种就是使用name+params,不过那种方法需要在路由配置文件中指定某路径的name。

html部分:

<el-table
        :data="tableData"
        @row-click="showDetail"
        :row-style="{height: '60px'}"
        >
        <el-table-column prop="title" label="标题" width="160">
        </el-table-column>
        <el-table-column prop="meeting" label="所属会议" width="100">
        </el-table-column>
        <el-table-column prop="postDate" label="发布时间" width="100">
        </el-table-column>
        <el-table-column prop="keyword" label="关键字" width="230" :show-overflow-tooltip='true'>
        </el-table-column>
        <el-table-column prop="abstractContext" label="摘要" :show-overflow-tooltip='true'>
        </el-table-column>
      </el-table>

JavaScript部分:

export default {
  data(){
    return {
      tableData:''
    }
  }
  
  methods : {    
    showDetail(row, column, event) {
      this.selectedObj=row
      let str=JSON.stringify(this.selectedObj)
      this.$router.push({path:'PaperDetail',query:{selectedObj:str}})
    }
}

EChart实现:

1.axios会一次性把三个会议的数据都从后端传过来,然后在接收到数据后会解析数据并根据html中的id来填入相应的表格。
2.getYear()和getCount()主要是因为后台传过来的数据是json文件,可是EChart填入数据时可能只需要其中每个对象的某一个属性(比如X轴只需要每个对象的年份),所以使用es6的map语法进行了映射,构造合适的新数组。
3.还有一个需要注意的地方就是自定义的toolTip,trigger: 'axis'表示鼠标每移到一个新的横坐标上时会触发下面的formatter函数,该函数的参数中params封装了series里的data数据。这边有个很值得注意的地方是series中是可以传入对象数组的,在渲染图表时,EChart只会获取每个对象的value属性,如果封装了其他属性的话就可以在formatter函数的param参数中获取到并且使用,这样可以给图表添加很多其他的信息,而不再只是简单的二维。
比如这里就把X轴是年份,Y轴是引用次数,想要在图表上反映出相应的关键词信息的话,就使用了formatter函数。
4.记得设置CSS样式!没设置长宽的话图表是显示不出来的!

html部分

<div id="CVPR" class="myChart">

JavaScript部分:

import axios from 'axios'
//echart5.0以上不能用import echarts from 'echarts'
import * as echarts from 'echarts'
export default {
data () {
  return {
    myChart:'',
  }
},
methods: {
  getChartInfo () {
    axios.get('http://localhost:5000/chartInfo').then((res) => {
      for(var i=0;i<res.data.length;i++){
        this.chartData = res.data[i]
        this.draw(this.TYPE[i])
      }
    })
  },
  getyears(){
    let arrnew = [];
    this.chartData.forEach(e => {
        arrnew.push(e.year)
    })
    return arrnew
  },
  getCount(){
    let arrnew = this.chartData.map((item,index) => {
        return Object.assign(this.chartData[index],{'value':item.count})
    })
    return arrnew
  },
  draw(type){
    //省略了部分样式代码
    this.myChart = echarts.init(document.getElementById(type))
    let option = null
    option = {
        title: {
            text: type
        },
        tooltip: {
            show: true,
            trigger: 'axis',
            formatter:function(params){
              return "关键字: "+params[0].data.keyword+"<br/>引用次数: "+params[0].data.count
            },
        },
        xAxis: {
            type: 'category',
            boundaryGap: false,
            data: this.getyears()
        },
        yAxis: {
            type: 'value'
        },
        series: [
            {
                name: type,
                type: 'line',
                data: this.getCount()
            },
        ]
    }
    this.myChart.setOption(option)
  }
},
mounted() {
  this.getTotalRank()
  this.getChartInfo()
}
}

CSS样式:

.myChart {
   width: 95%;
   height: 350px;
   margin: 20px auto;
   border: 1px solid #CCC;
}

后端部分

收到来自前端传来的关键字,在数据库中进行查找,并将论文数据转化为Json格式

    def getItems(self, keyword=None):
        if keyword:
            sql = "select * from paper" + " where title like '%" + keyword + "%' or keyword like '%" + keyword + "%'"
        self.cursor.execute(sql)
        items = self.cursor.fetchall()
        papers = []
        for r in items:
            paper = {}
            paper['id'] = r[0]
            paper['title'] = r[1]
            paper['abstractContext'] = r[2]
            paper['typeandyear'] = r[3]
            paper['keyword'] = r[4]
            paper['releasetime'] = r[5]
            paper['link'] = r[6]

            papers.append(paper)
        items = json.dumps(papers)

        return items

获取总热词TOP10,将数据以json格式发送

    def getTotalRank(self):
        datalist = []
        SQL = "SELECT keyword,frequency FROM `article`.`keywordanalysis`" \
              " WHERE `type` LIKE 'all' ORDER BY `frequency` DESC LIMIT 10"
        self.cursor.execute(SQL)
        alldata = self.cursor.fetchall()
        datalist = list(alldata)

        papers = []
        for r in alldata:
            paper = {}
            paper['keyword'] = r[0]
            paper['count'] = r[1]
            papers.append(paper)
            continue

        paperJSON = json.dumps(papers)
        return paperJSON

接收删除指令,对数据库进行删除操作,并返回一个值确定是否删除成功

    def deletePaper(self, paperID):
        SQL = "DELETE FROM paper WHERE `id` = '" + paperID + "'"
        rownumber = self.cursor.execute(SQL)
        self.conn.commit()
        result = {}
        results = []
        if rownumber:
            result['state'] = "1"
        else:
            result['state'] = "0"

        results.append(result)
        resJSON = json.dumps(results)
        return resJSON

心路历程及收获

221801404的心路历程:

这一次的作业不同于上一次的原型设计,这一次需要前后端进行对接,所以在这个问题上也花了挺多时间去查找资料,而且两个人合作的方式对于我来说有着一种督促的作用,毕竟这个作业不只是我一个人的事,所以在潜意识里就会给自己一些压力,想要去做好,不给队友拖后腿。我觉得这是一次很有意义的作业,让我体会到了合作带来的一系列感受。我感觉挺好的,收获还是挺多的。

221801421的心路历程:

对于vue框架各部分的相互作用有了更深刻的理解,对elementUI这种组件库的使用也有了认识,同时熟悉了与后端的交互流程。

这一路走下来的难点主要有以下两方面:

  1. 环境的配置和导入:

框架的安装,elementUI、axios、echart的导入,就算按照官方文档也没有一个是顺利的,总之就是各种报错。比如npm install时因为墙的原因无法安装,只能去找镜像;elementUI按需引入直到最后也没有成功,只能退而求其次进行全局引入,比如echart5.0以上必须使用import * as echarts from 'echarts'这个语句才能正常使用……每次使用新技术都是这些配置最叫人头疼,写代码时还有办法能调试,但是这配置环境时有时明明依赖里都有相应的库了却还是用不了报红。

总结了一下这种情况的解决方法:

  • 确定已经导入了依赖库
  • 搜索解决方法时带上依赖库具体版本对症下药
  • 除了CSDN等开发者平台,还可以去官方的讨论区找找(如果有讨论区的话)。
  • 去找配置成功的人请教,不只是因为对方有配置经验,有时可能只是简单的拼写错误,但自己不管怎么看都发现不了,这是就需要一个旁观者来帮自己检查一下。
  • 如果都不行就不要下最新版本,去找别人已经确认了的,成功过的教程的版本。
  1. 组件库的使用:

这是我第一次在项目中系统地使用组件库,最开始的时候看到官方文档中对于每个组件就只有一段引用代码,而且还是配合框架使用,感觉有点无从下手。而我的解决方法就是从最简单的部件先一点一点测试,比如先放一个按钮,然后在methods中设置点击事件,成功以后换其他的接下来要使用的部件,一步一步来。
中间碰到的最难的地方就是官方文档中并没有写清楚回调函数中每个参数的作用,所以只能自己测,将它们一个个打印出来,然后再拿到自己想要的数据。

给队友的评价

221801404对221801421的评价:

他是一个很有自己想法的人,同时在结对过程中也给予了我很多帮助,在对数据格式转化的时候,他也有在给我提供思路,还帮我找了一些资料(我也有再找,并不是我懒),我挺愧疚的,毕竟在任务分配时我是后端,他是前端。他很好说话,对我的帮助也很大。在对接的时候也是很积极地与我进行讨论,并且给了我很多有用的建议。

221801421对221801404的评价:

和好兄弟的交流很愉快,在结对之初就来和我商讨json文件的格式,前后端对接时使用他的电脑,因为之前的沟通准备很完全,所以对接的非常顺利。遇到问题时会积极思考解决方法,遇到难以解决的需求会和我前端商讨妥协之法,这次开发能够完成多亏了好兄弟的努力。

posted @ 2021-03-31 13:58  抹布拉布  阅读(118)  评论(4编辑  收藏  举报