结对第二次作业
这个作业属于哪个课程 | 2021春软件工程实践S班 (福州大学) |
---|---|
这个作业要求在哪里 | 结对作业二 |
结对学号 | 221801111 221801132 |
这个作业的目标 | 1、和伙伴通过github协作/代码规范等 2、实现结对作业一中原型中的部分功能 3、将项目部署到云服务器上 |
其他参考文献 | CSDN、博客园 |
Github仓库地址和代码规范链接
PSP表格和效能分析
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 60 |
• Estimate | • 估计这个任务需要多少时间 | 60 | 60 |
Development | 开发 | 2620 | 2680 |
• Analysis | • 需求分析 (包括学习新技术) | 600 | 550 |
• Design Spec | • 生成设计文档 | 120 | 100 |
• Design Review | • 设计复审 | 60 | 100 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 20 | 20 |
• Design | • 具体设计 | 500 | 470 |
• Coding | • 具体编码 | 900 | 1080 |
• Code Review | • 代码复审 | 240 | 180 |
• Test | • 测试(自我测试,修改代码,提交修改) | 180 | 180 |
Reporting | 报告 | 150 | 140 |
• Test Repor | • 测试报告 | 60 | 70 |
• Size Measurement | • 计算工作量 | 30 | 20 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 60 | 50 |
合计 | 2830 | 2880 |
访问链接
项目地址
ps:我们的服务器响应时间过长,搜索之后要过很久才能显示
成品展示
-
首页
-
论文列表
-
数据分析页
-
关键词图谱
-
包含关键词的论文列表
-
热词动图
-
搜索和删除
- 热词动图
- 关键词图谱
结对讨论过程描述
这次结对作业是对结对作业一的原型的部分功能实现。
基础功能:
功能一:对已爬取的论文列表进行操作
1、对论文列表进行删除
2、对论文列表进行查询详细信息(支持模糊查询)
功能二:分析已爬取到的论文信息,提取top10个热门领域或热门研究方向
1、关键词图谱:点击某个关键词可展现相关的论文
2、多年间、不同顶会的热词呈现热度走势对比动
分析了基础功能之后,我们决定用刚刚学的JSP、Servlet,基于Web来开发。对论文列表的展示用JSP来显示,论文列表的删除、查询用Servlet来处理。上网查阅了一些资料后,我们觉得关键词图谱、热词动图可以用echart来实现展示。K负责Servlet部分的代码编写,L负责JSP部分的代码编写。同一个功能模块的JSP和Servlet代码同步进行编写,这样可以避免前后端后期再集中整合的冲突。
两人结对讨论的截图
设计实现过程
论文列表的数据需要一个Paper对象类来表示,数据库的CRUD用DAO层的接口类来实现。论文列表的展示用一个PaperList.jsp来显示,JSP提交表单后ListServlet处理数据,执行sql语句,再返回数据给JSP显示出论文列表。删除的功能与查询类似。
热词的数据用一个HotWord来表示,数据库的CRUD用DAO层的接口类来实现。三个会议的热词动图分成三个页面,页面的实现代码一样,点击选项之后,提交表单后ChartServlet处理数据,执行sql语句,返回数据给JSP,再展示成一个时间轴柱状图。
关键词图谱目前只是使用按钮来实现跳转,点击一个关键词之后,提交表单后KWCServlet处理数据,执行sql语句,传递数据的KeyWordList.jsp显示相关的论文列表。
- 数据库表设计
功能结构图
关键代码
- 数据库连接接口
public class DBUtil {
static String ip = "127.0.0.1";
static int port = 3306;
static String database = "paper";
static String encoding = "UTF-8";
static String loginName = "root";
static String password = "lj3990353";
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
String url = String.format("jdbc:mysql://%s:%d/%s?characterEncoding=%s&useSSL=false", ip, port, database, encoding);
return DriverManager.getConnection(url, loginName, password);
}
/* 关闭连接的方法 */
public static void close(ResultSet rs, Statement stmt, Connection conn) {
try {
if (rs != null)
rs.close();
} catch (Exception ex) {
ex.printStackTrace();
}
try {
if (stmt != null)
stmt.close();
} catch (Exception ex) {
ex.printStackTrace();
}
try {
if (conn != null)
conn.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) throws SQLException {
System.out.println(getConnection());
}
}
- 数据库按论文标题模糊搜索
用sql语句的like可以筛选出包含搜索词的数据项,就可以实现模糊搜索了
public List<Paper> listGetByTitle(String str) {
String sql = "select * from post where title " + "like" +"'%" + str + "%'";
try (Connection c = DBUtil.getConnection(); PreparedStatement ps = c.prepareStatement(sql)){
ResultSet rs = ps.executeQuery(sql);
List<Paper> list = new ArrayList<>();
while (rs.next()) {
Paper p = new Paper();
p.setTitle(rs.getString(1));
p.setSummary(rs.getString(2));
p.setLink(rs.getString(3));
p.setKeyword(rs.getString(4));
p.setYear(rs.getString(5));
p.setType(rs.getString(6));
list.add(p);
}
return list;
} catch (SQLException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
return null;
}
- 获取论文列表
实例化一个PaperDAO对象并调用PaperDAOImpl这个接口的listGetByTitle()方法,获取按标题搜索的论文列表,再传给jsp来显示
public class ListServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PaperDAO paperDAO = new PaperDAOImpl();
String option = request.getParameter("option");
String text = request.getParameter("search");
List<Paper> list = new ArrayList<>();
if ("title".equals(option)) {
list = paperDAO.listGetByTitle(text);
}
if ("keyword".equals(option)) {
list = paperDAO.listGetByKeyword(text);
}
if ("type".equals(option)) {
list = paperDAO.listGetByType(text);
}
request.setAttribute("list", list);
request.getRequestDispatcher("PaperList.jsp").forward(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
- 显示论文列表
用HTML的table来显示Servlet处理后传来的论文列表数据
<%!
List<Paper> list = new ArrayList<>();
%>
<%
list = (List<Paper>) request.getAttribute("list");
if(list != null) {
%>
<table border="1" width="400" data-pagination="true"
data-side-pagination="client"
data-page-size="3">
<tr>
<th width="200px">论文标题</th>
<th width="600px">摘要</th>
<th>原文链接</th>
<th width="200px">关键词</th>
<th>年份</th>
<th>类别</th>
<th>操作</th>
</tr>
<%
for (Paper paper : list) {
%>
<tr>
<td width="200px"><%=paper.getTitle() %></td>
<td width="600px"><%=paper.getSummary() %></td>
<td><a href=<%=paper.getLink() %>> <%=paper.getLink() %></a></td>
<td width="200px"><%=paper.getKeyword() %></td>
<td><%=paper.getYear() %></td>
<td><%=paper.getType() %></td>
<td>
<form method="post" id="form1" action="<%=path%>/DeleteServlet">
<input type="submit" name="deleteButton" value="删除">
<input type="hidden" id="title" name="title" value="<%=paper.getTitle() %>">
</form>
</td>
</tr>
<%
}
}
%>
</table>
- 获取热词
用listGetByYearCVPR()获得每年的前十个热词,再传给jsp展示
List<HotWord> total = new ArrayList<>();
int cnt = 0;
List<HotWord> CVPRHotWordList = new ArrayList<>();
for (int year = 2000;year < 2021;year++) {
WordDAO wordDAO = new WordDAOImpl();
List<KeyWord> keyWordList = new ArrayList<>();
keyWordList = wordDAO.listGetByYearCVPR(Integer.toString(year));//关键词列表
//if (keyWordList == null) System.out.println("空");
if (keyWordList != null) {
Map<String,Integer> map = new HashMap<String,Integer>();//创建map,key保存字符串,value保存出现的次数
for (KeyWord word : keyWordList) {
String s = word.getWord();
String[] temp = strCut(s);
int size = temp.length;
for (int i = 0; i < size; i++) {//遍历temp数组
if (map.containsKey(temp[i])) {
map.put(temp[i], map.get(temp[i]) + 1);
}
else
map.put(temp[i],1);
}
}
map = sortByValueDescending(map);
List<HotWord> HotWordList = new ArrayList<>();
for (Map.Entry<String,Integer> vo : map.entrySet()) {
HotWord hotWord = new HotWord();
hotWord.setWord(vo.getKey());
hotWord.setNum(vo.getValue());
hotWord.setYear(Integer.toString(year));
hotWord.setType("CVPR");
HotWordList.add(hotWord);
}
HotWord[] hotWord = new HotWord[10];
for (int i = 0;i < 10;i++) {
hotWord[i] = HotWordList.get(i);
}
for (int i = 0;i < 10;i++) {
CVPRHotWordList.add(hotWord[i]);//CVPR每年的热词
}
}
for (int i = 0;i < 10;i++) {
total.add(CVPRHotWordList.get(i));
}
}
- 用echart展示热词动图
用echart的时间轴实现每年的热词展示,展示形式为柱状图
<div id="main" style="width: 1280px;height:600px;"></div>
<script type="text/javascript">
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 指定图表的配置项和数据
var year = ['2016','2018','2020'];
var word = ['Learning','Image','Neural','Convolut','Network','Deep','Object'];
var option = {
timeline:{
axisType: 'category',
autoPlay: true,
playInterval: 1500,
data: year,
label:{
fontSize: 18
}
},
baseOption:{
dataset:{
source:[
['year','Learning','Image','Neural','Convolut','Network','Deep','Object'],
['2016',81,73,63,55,55,50,42],
['2018',313,160,145,94,138,174,106],
['2020',449,250,141,53,118,150,151]
]
},
title: {
text: 'ECCV热词热度走势图',
left: 'center',
textStyle:{
fontSize:15
}
},
tooltip: {
trigger: 'axis'
},
toolbox: {
feature: {
saveAsImage: {}
}
},
legend: {
},
grid: {
left: '10%',
bottom: '15%',
containLabel: true
},
xAxis: [
{
type:'category',
data: word,
axisPointer: {
type: 'shadow'
},
},
],
yAxis:[
{
name:'热度',
type:'value',
nameTextStyle:{
fontSize:18
},
axisLabel:{
fontSize:18
}
},
],
series: [
{
//name: 'GDP',
type: 'bar',
seriesLayoutBy: 'row',
encode:{
x:'year',
y:'2016'
}
},
]
},
options:[]
};
for (var n = 0; n < year.length; n++){
option.options.push({
title:{
show:true,
left: 'center',
textStyle:{
fontSize:24
}
},
series:[
{
type: 'bar',
seriesLayoutBy: 'row',
encode:{
x:'year',
y:year[n]
}
},
]
});
}
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
</script>
结对开发项目的心路历程与收获
心路历程与收获
K:写完代码的时候,心里五味杂陈的,心里想终于把项目的要求功能实现了,写完了,终于写完了。。。每天晚上一两点上床,白天没上课的时候要用5-6个小时来写代码(主要还是我太菜了),写完了想想这些好像也没什么。这次的项目基于Web来开发,我也是刚刚开始学JSP、Servlet,spring都还没接触,JSP、Servlet从模糊了解,到熟悉使用,都是磕磕碰碰。网上看视频,找教程,学习新技术真的占了开发的一大半时间。还有echart的使用,在同学的帮助下,也才会使用一点。Github工具现在基本上会使用了,也达到了熟练的程度,对协同开发也可以轻车熟路。在编码的过程中,我感到了深深的痛苦,每次写完一个功能测试的时候,总是有问题(头都大了),找到bug再修复的过程真的是让我处于崩溃的边缘,每次修复完之后的心情真的是心里的石头终于落下了(QAQ)。
L:刚开始看到题目的时候,我觉得有点头大,因为这次的作业不仅有很多我不熟悉的技术,而且分工的问题也很麻烦。本来想我做前端、K做后端。但是我们使用的JSP和Servlet前后端并没有很好的分离。难免遇到分工不均的问题,我也觉得这次K比我更多的时间在写代码上,所以我也更积极做一些其他的工作,比如部署服务器、撰写博客等。在做这次作业的这几天,我们每天花了几个小时在学习新技术,早上利用上完课挤出的零碎时间学习,晚上经常写代码到半夜,遇到不懂的问题也通过互相讨论、上网学习、询问其他的同学解决,虽然很累,但是觉得非常充实。经过这次的作业,我觉得自己的JavaWeb水平有了一定的提升、github和idea等工具的使用也更加熟练,也更加懂得如何去团队协作了。
评价结对队友
K->L:这次结对开发过程中有些工作我是需要让L来做的,他完成的不错,让我觉得这次结对作业轻松了不少。我的伙伴对这次结对作业很有热情、态度也十分认真,经常写代码到深夜,结对开发麻烦就麻烦在需要沟通与理解,所以我希望我的伙伴可以对结对开发过程的讨论更积极一些,也可以在开发过程中向我积极反馈问题,及时解决。
L->K:这次结对作业,K负责了工作量比较大的后端,包括数据库和数据分析等部分,但他也没有怨言,态度十分认真,积极完成了他负责的部分,我也觉得他做的很好。但是因为前几天K在忙比赛的事情,这次作业开始的时间比较晚,所以这次作业做的有些匆忙,有些细节没有很好地完善。我希望下次作业K能尽快安排时间开始做,让时间更充裕一些。