這個作業屬於哪個課程 | 2021春軟件工程實踐S班 (福州大學) |
---|---|
這個作業要求在哪里 | 作業連接 |
結對學號 | 221801135 & 221801114 |
這個作業的目標 | 頂會熱詞統計的實現 |
其他參考文獻 | 《碼出高效_阿里巴巴Java開發手冊》 |
一、git倉庫鏈接和代碼規范鏈接
git倉庫鏈接
代碼規范鏈接
二、PSP表格
PSP2.1 | Personal Software Process Stages | 預估耗時(分鍾) | 實際耗時(分鍾) |
---|---|---|---|
Planning | 計划 | 15 | 20 |
• Estimate | • 估計這個任務需要多少時間 | 15 | 20 |
Development | 開發 | 1600 | 1680 |
• Analysis | • 需求分析 (包括學習新技術) | 60 | 50 |
• Design Spec | • 生成設計文檔 | 30 | 30 |
• Design Review | • 設計復審 | 50 | 40 |
• Coding Standard | • 代碼規范 (為目前的開發制定合適的規范) | 30 | 30 |
• Design | • 具體設計 | 300 | 390 |
• Coding | • 具體編碼 | 960 | 990 |
• Code Review | • 代碼復審 | 50 | 50 |
• Test | • 測試(自我測試,修改代碼,提交修改) | 120 | 100 |
Reporting | 報告 | 80 | 75 |
• Test Repor | • 測試報告 | 30 | 35 |
• Size Measurement | • 計算工作量 | 20 | 15 |
• Postmortem & Process Improvement Plan | • 事后總結, 並提出過程改進計划 | 30 | 25 |
合計 | 1695 | 1775 |
三、成品展示
雲服務器的訪問鏈接
-
初始頁面:
-
論文列表頁
列表左上角顯示了一共有多少篇論文。點擊列表項可以進入論文詳情頁。
列表實現了分頁功能,也能跳轉到指定的頁面。
論文列表右上角的放大鏡圖標可以喚出搜索框,輸入內容可以實現對論文標題的模糊查詢。如果搜索框里的內容是空串時,搜索的論文為全部已有論文
點擊論文項右側的刪除圖標可以刪除該論文。
- 論文詳情頁
論文詳情頁展示了該論文的詳細信息,包括標題、原文鏈接、年份、會議、關鍵詞和摘要。點擊左上角的返回圖標可以返回剛剛的論文列表。點擊某一個關鍵詞可以顯示擁有該關鍵詞的論文列表。
點擊原文鏈接可以跳轉到這篇論文的原鏈接。
- 關鍵詞圖譜頁
點擊圖表下方的按鈕可以控制顯示哪幾個關鍵詞。
點擊某一個關鍵詞可以顯示擁有該關鍵詞的論文列表。
- 熱詞走勢圖界面
以折線圖或者柱狀圖的形式展現數據,鼠標移動到上面可以顯示具體數據。
點擊圖表上方的按鈕可以選擇不展示哪一個會議的數據。
點擊下方的關鍵詞按鈕可以選擇展示某一個關鍵詞的相關數據。
可以通過點擊圖表右上方的按鈕切換圖表樣式。
四、結對討論過程描述
進行本次結對作業時已經返校了,而且我們是舍友,所以我們主要采用的時線下討論的方式來完成結對編程。
我們首先討論了采用什么技術實現這一次的作業,最后決定采用JSP和Servlet實現。
然后是關鍵詞圖譜和熱度走向圖的實現部分,剛開始是打算使用自己編寫的圖表實現,因為開始使用Echarts的時候沒辦法進行點擊跳轉頁面,所以關鍵詞圖表我們是自己編寫的,而熱度走向圖則采用Echarts提供的動態圖表實現。
以下是功能對接時的聊天記錄截圖:
五、設計實現過程
設計實現過程
- 本次的原型實現是基於Web來開發的,使用了Web框架;
- 前端主要采用JSP來編寫界面布局,后端使用JAVA語言編寫,使用JDBC與數據庫進行連接,通過Servlet使用url來與前端的JSP進行對接,轉遞數據庫相關數據;
- 圖表部分使用了Echarts提供的動態圖表,能夠實現動態顯示與點擊跳轉的功能。
功能結構圖
六、代碼說明
- 論文列表實現
-
前端代碼
獲取從ListServlet處得到的當前頁面的論文列表,並顯示在頁面上。
<body>
<div>
<form class="searchBox_listPage" method="get" action="/partnerwork_war_exploded/SearchServlet">
<input class="searchBox_text" type="text" name="search" placeholder="Type to search">
<input type="submit" class="searchBox_btn" value="">
</form>
</div>
<a class="title_otherPage" href="mainPage.jsp">Crwaler</a>
<a class="leftpage_btn" href="/partnerwork_war_exploded/ChartPageServlet"><img src="png/leftpage.png"></a>
<a class="rightpage_btn" href="trendPage.jsp"><img src="png/rightpage.png"></a>
<%
out.print("<div class=\"totalNumber\">共查詢到"+request.getAttribute("totalNumber")+"篇論文</div>");
%>
<ul class="main">
<%
List<Paper> list = (List<Paper>) request.getAttribute("list");
int iterator=(int)request.getAttribute("iterator");
for (int i=0;i<list.size();++i)
{
out.print("<li class=\"single_paper boxshadow\">" +
"<a class=\"paper_number\">"+(iterator+i+1)+"</a>"+//顯示數字比迭代器多+1
"<a class=\"paper_title\" href=\"/partnerwork_war_exploded/InfoServlet?iterator="+(iterator+i)+"\">"+list.get(i).getTitle()+"</a>"+
"<div class=\"buttonbox\">" +
"<a class=\"delete_btn\" onclick=\"func("+(iterator+i)+","+request.getAttribute("currentPage")+")\">" +
"<img src=\"png/delete.png\">" +
"</a>" +
"</div>" +
"</li>");
}
%>
<li class="single_paper">
<%
out.print("<a class=\"lastpage_btn\" href=\"/partnerwork_war_exploded/LastPageServlet?page="+((int) request.getAttribute("currentPage") - 1)+"\">\n");
%>
<img src="png/lastpage.png">
</a>
<div class="pagenumber">
<%
out.print("<form method=\"get\" action=\"/partnerwork_war_exploded/SkipServlet?currentPage="+request.getAttribute("currentPage")+"\"><input type=\"text\" name=\"page\" class=\"currentpage\" placeholder=\""+request.getAttribute("currentPage")+"\"/></form>");
%>
/<a class="maxpage">
<%
int num=1;
if(request.getAttribute("maxPage")!=null)
num=(int)request.getAttribute("maxPage");
out.print(num);
%>
</a>
</div>
<%
out.print("<a class=\"nextpage_btn\" href=\"/partnerwork_war_exploded/NextPageServlet?page=" + ((int) request.getAttribute("currentPage") + 1) + "\">\n");
%>
<img src="png/nextpage.png">
</a>
</li>
</ul>
<script>
function func(iterator,page){ /* 綁定事件 */
var r = confirm("確定從庫中移除該論文?")
if (r == true) {
var str="/partnerwork_war_exploded/DeleteServlet?iterator="+iterator+"&page="+page;
window.location.href=str;
}
}
</script>
</body>
- 后端代碼
后端通過list()函數獲取數據庫中的論文,再以重載函數list(int page, int count)獲得某一頁的制定數量的文論列表通過Servlet轉遞給前端的JSP,實現分頁的功能。
public List<Paper> list(){
paperList = new ArrayList<Paper>();
try (Connection c = DBUtil.getConnection(); Statement s = c.createStatement()) {
String sql = "select * from paper";
ResultSet rs = s.executeQuery(sql);
while (rs.next()) {
Paper paper = new Paper();
paper.setId(rs.getInt("id"));
paper.setTitle(rs.getString("title"));
paper.setAbstractText(rs.getString("abstract"));
paper.setKeywords(rs.getString("keywords"));
paper.setTagList();
paper.setDoiLink(rs.getString("doiLink"));
paper.setPublicationDate(rs.getInt("publicationDate"));
paper.setConference(rs.getString("conference"));
paperList.add(paper);
}
} catch (SQLException e) {
e.printStackTrace();
}
return paperList;
}
public List<Paper> list(int start, int count){
if (paperList == null){
list();
setTagMap();
}
List<Paper> list = new ArrayList<Paper>();
if (start * count > paperList.size()){
for (int i = (start - 1) * count;i < paperList.size();i ++){
list.add(paperList.get(i));
}
}
else{
list = paperList.subList((start - 1) * count , (start - 1) * count + count);
}
return list;
}
- 熱詞統計實現
- 前端代碼
以柱狀圖表的形式表現出爬取論文中的top10個關鍵詞,從ChartPageServlet獲取傳來的排序過的關鍵詞列表,並逐個顯示在頁面上。
<body>
<a class="title_otherPage" href="mainPage.jsp">Crwaler</a>
<a class="leftpage_btn" href="trendPage.jsp"><img src="png/leftpage.png"></a>//頁面切換到trendPage
<a class="rightpage_btn" href="/partnerwork_war_exploded/ListServlet"><img src="png/rightpage.png"></a>
<div class="main">
<div class="chartname">熱詞統計</div>
<div class="selectbox">
<input type="checkbox" name="select" id="slide_1" class="checkbox" checked>
<input type="checkbox" name="select" id="slide_2" class="checkbox" checked>
<input type="checkbox" name="select" id="slide_3" class="checkbox" checked>
<input type="checkbox" name="select" id="slide_4" class="checkbox" checked>
<input type="checkbox" name="select" id="slide_5" class="checkbox" checked>
<input type="checkbox" name="select" id="slide_6" class="checkbox" checked>
<input type="checkbox" name="select" id="slide_7" class="checkbox" checked>
<input type="checkbox" name="select" id="slide_8" class="checkbox" checked>
<input type="checkbox" name="select" id="slide_9" class="checkbox" checked>
<input type="checkbox" name="select" id="slide_10" class="checkbox" checked>
<div class="chart">
<%
/*這個地方獲取tag列表然后下面每一個地方都要放tag的具體內容,鏈接跳轉到searchServler去進行搜索然后重定向到listPage*/
List<HashMap.Entry<String, Integer>> list = (List<HashMap.Entry<String, Integer>>) request.getAttribute("list");
out.print("<div class=\"chart_slide\" id=\"slide_chart_1\" onclick=\"document.location.href='/partnerwork_war_exploded/TagSearchServlet?search="+list.get(0).getKey()+"'\">"+list.get(0).getKey()+"<br>"+list.get(0).getValue()+"</div>\n" +
"<div class=\"chart_slide\" id=\"slide_chart_2\" onclick=\"document.location.href='/partnerwork_war_exploded/TagSearchServlet?search="+list.get(1).getKey()+"'\">"+list.get(1).getKey()+"<br>"+list.get(1).getValue()+"</div>\n" +
"<div class=\"chart_slide\" id=\"slide_chart_3\" onclick=\"document.location.href='/partnerwork_war_exploded/TagSearchServlet?search="+list.get(2).getKey()+"'\">"+list.get(2).getKey()+"<br>"+list.get(2).getValue()+"</div>\n" +
"<div class=\"chart_slide\" id=\"slide_chart_4\" onclick=\"document.location.href='/partnerwork_war_exploded/TagSearchServlet?search="+list.get(3).getKey()+"'\">"+list.get(3).getKey()+"<br>"+list.get(3).getValue()+"</div>\n" +
"<div class=\"chart_slide\" id=\"slide_chart_5\" onclick=\"document.location.href='/partnerwork_war_exploded/TagSearchServlet?search="+list.get(4).getKey()+"'\">"+list.get(4).getKey()+"<br>"+list.get(4).getValue()+"</div>\n" +
"<div class=\"chart_slide\" id=\"slide_chart_6\" onclick=\"document.location.href='/partnerwork_war_exploded/TagSearchServlet?search="+list.get(5).getKey()+"'\">"+list.get(5).getKey()+"<br>"+list.get(5).getValue()+"</div>\n" +
"<div class=\"chart_slide\" id=\"slide_chart_7\" onclick=\"document.location.href='/partnerwork_war_exploded/TagSearchServlet?search="+list.get(6).getKey()+"'\">"+list.get(6).getKey()+"<br>"+list.get(6).getValue()+"</div>\n" +
"<div class=\"chart_slide\" id=\"slide_chart_8\" onclick=\"document.location.href='/partnerwork_war_exploded/TagSearchServlet?search="+list.get(7).getKey()+"'\">"+list.get(7).getKey()+"<br>"+list.get(7).getValue()+"</div>\n" +
"<div class=\"chart_slide\" id=\"slide_chart_9\" onclick=\"document.location.href='/partnerwork_war_exploded/TagSearchServlet?search="+list.get(8).getKey()+"'\">"+list.get(8).getKey()+"<br>"+list.get(8).getValue()+"</div>\n" +
"<div class=\"chart_slide\" id=\"slide_chart_10\" onclick=\"document.location.href='/partnerwork_war_exploded/TagSearchServlet?search="+list.get(9).getKey()+"'\">"+list.get(9).getKey()+"<br>"+list.get(9).getValue()+"</div>");
%>
</div>
<div class="slide">
<label for="slide_1" id="slide_btn_1">TOP 1</label>
<label for="slide_2" id="slide_btn_2">TOP 2</label>
<label for="slide_3" id="slide_btn_3">TOP 3</label>
<label for="slide_4" id="slide_btn_4">TOP 4</label>
<label for="slide_5" id="slide_btn_5">TOP 5</label>
<label for="slide_6" id="slide_btn_6">TOP 6</label>
<label for="slide_7" id="slide_btn_7">TOP 7</label>
<label for="slide_8" id="slide_btn_8">TOP 8</label>
<label for="slide_9" id="slide_btn_9">TOP 9</label>
<label for="slide_10" id="slide_btn_10">TOP 10</label>
</div>
</div>
</div>
</body>
- 前端代碼
首先setTagMap()函數了統計爬取論文的關鍵詞,保存在一個Map中,再使用setSortTagList()函數對關鍵詞進行排序,排序結果保存在sortTagList中,並只取sortTagList的前十項。
public void setTagMap(){
tagMap = new HashMap<String, Integer>();
for (Paper paper : paperList){
for (String tag : paper.getTagList()){
tag = tag.toLowerCase();
if (tagMap.containsKey(tag)) {
int n = tagMap.get(tag);
tagMap.put(tag, n + 1);
} else {
tagMap.put(tag, 1);
}
}
}
}
public void setSortTagList(){
sortTagList = new ArrayList<>(tagMap.entrySet());
Collections.sort(sortTagList, new Comparator<HashMap.Entry<String, Integer>>() {
@Override
public int compare(HashMap.Entry<String, Integer> tag1, HashMap.Entry<String, Integer> tag2) {
if (tag1.getValue().equals(tag2.getValue())) {
return tag1.getKey().compareTo(tag2.getKey());
} else {
return tag2.getValue() - tag1.getValue();
}
}
});
sortTagList = sortTagList.subList(0, 10);
}
public List<HashMap.Entry<String, Integer>> getSortTagList() {
return sortTagList;
}
- 熱度走向圖實現
- 前端代碼
使用Echarts來展示top10個關鍵詞在近幾年三大會議的出現的次數,通過json文件解析所需要的數據展示在圖表上。
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="css/trendPage.css">
<title>Trend Page</title>
<script src="https://cdn.bootcdn.net/ajax/libs/echarts/5.0.2/echarts.common.js"></script>
</head>
<body>
<a class="title_otherPage" href="mainPage.jsp">Crwaler</a>
<a class="leftpage_btn" href="/partnerwork_war_exploded/ListServlet"><img src="png/leftpage.png"></a>
<a class="rightpage_btn" href="/partnerwork_war_exploded/ChartPageServlet"><img src="png/rightpage.png"></a>
<div class="main">
<div class="selectbox">
<input type="radio" name="select" id="slide_1" class="checkbox" checked>
<input type="radio" name="select" id="slide_2" class="checkbox" >
<input type="radio" name="select" id="slide_3" class="checkbox" >
<input type="radio" name="select" id="slide_4" class="checkbox" >
<input type="radio" name="select" id="slide_5" class="checkbox" >
<input type="radio" name="select" id="slide_6" class="checkbox" >
<input type="radio" name="select" id="slide_7" class="checkbox" >
<input type="radio" name="select" id="slide_8" class="checkbox" >
<input type="radio" name="select" id="slide_9" class="checkbox" >
<input type="radio" name="select" id="slide_10" class="checkbox" >
<div id="chartbar1" class="chartbar"></div>
<div id="chartbar2" class="chartbar"></div>
<div id="chartbar3" class="chartbar"></div>
<div id="chartbar4" class="chartbar"></div>
<div id="chartbar5" class="chartbar"></div>
<div id="chartbar6" class="chartbar"></div>
<div id="chartbar7" class="chartbar"></div>
<div id="chartbar8" class="chartbar"></div>
<div id="chartbar9" class="chartbar"></div>
<div id="chartbar10" class="chartbar"></div>
<div class="slide">
<%
PaperDao paperDao = PaperDao.getInstance();
if (paperDao.getSortTagList() == null){
paperDao.setSortTagList();
}
List<HashMap.Entry<String, Integer>> list = paperDao.getSortTagList();
out.print("<label for=\"slide_1\" id=\"slide_btn_1\">"+list.get(0).getKey()+"</label>\n" +
" <label for=\"slide_2\" id=\"slide_btn_2\">"+list.get(1).getKey()+"</label>\n" +
" <label for=\"slide_3\" id=\"slide_btn_3\">"+list.get(2).getKey()+"</label>\n" +
" <label for=\"slide_4\" id=\"slide_btn_4\">"+list.get(3).getKey()+"</label>\n" +
" <label for=\"slide_5\" id=\"slide_btn_5\">"+list.get(4).getKey()+"</label>\n" +
" <label for=\"slide_6\" id=\"slide_btn_6\">"+list.get(5).getKey()+"</label>\n" +
" <label for=\"slide_7\" id=\"slide_btn_7\">"+list.get(6).getKey()+"</label>\n" +
" <label for=\"slide_8\" id=\"slide_btn_8\">"+list.get(7).getKey()+"</label>\n" +
" <label for=\"slide_9\" id=\"slide_btn_9\">"+list.get(8).getKey()+"</label>\n" +
" <label for=\"slide_10\" id=\"slide_btn_10\">"+list.get(9).getKey()+"</label>");
%>
</div>
</div>
</div>
<script type="text/javascript">
var chartDom1 = document.getElementById('chartbar1');
var myChart1 = echarts.init(chartDom1,'dark');
var option1;
option1 = {
legend: {},
toolbox:{
show:true,
feature:{
restore:{
show:true},
magicType:{
type:['line','bar']
}
}},
title: {
text:'熱詞趨勢'
},
tooltip: {},
xAxis: [
{type: 'category'},
],
yAxis: {},
dataset: {
source: []
},
series: [
{type: 'bar', seriesLayoutBy: 'row'},
{type: 'bar', seriesLayoutBy: 'row'},
{type: 'bar', seriesLayoutBy: 'row'},
]
};
option1 && myChart1.setOption(option1);
$.get('../json/tag1.json').done(function(data){
var obj = $.parseJSON(data);
myChart1.setOption({
source:[obj.source]
}
);
});
/*剩余9個圖表的數據填充操作,同上,篇幅限制,就不展示了*/
</script>
</body>
</html>
- 后端代碼
通過循環來向數據庫查詢某一個關鍵詞在指定的幾年內在三大會議中出現的次數,存放在一個二維數組中並返回。
public int[][] countTagByYear(int[] years, String tag){
int[][] counts = new int[3][3];
String[] conferences = new String[]{"CVPR","ICCV","ECCV"};
try (Connection c = DBUtil.getConnection(); Statement s = c.createStatement()) {
for (int j = 0;j <= 2;j++){
for (int i = 0; i <= 2;i ++){
int total = 0;
String sql = "select count(*) from paper_library where lower(keywords) like '%" + tag +
"%' and (publicationDate="+years[i] + " or publicationDate=" + (years[i] + 1) +
") and conference ='" + conferences[j] + "'";
System.out.println(sql);
ResultSet rs = s.executeQuery(sql);
while (rs.next()) {
total = rs.getInt(1);
}
counts[j][i] = total;
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return counts;
}
七、心路歷程和收獲
221801135
說實話一開始接到這個任務的時候我是很懵的,好在第一次任務只是模型的構建,相對來說比較簡單,於是完成起來沒什么太大的壓力,但是轉而迎來了第二次作業,要求我們實現,突然就懵掉了,加上不久前剛剛經歷了24小時的極限編程,有些身心俱疲,還沒有調整過來,這次又是寫前端的,對於沒有框架經驗的我,直接用純html+css方式很難寫出好的頁面,雖然不用框架也避免了公式化頁面的結果,算是還原了自己原型設計的特色。后來經過了好幾天加班加點終於完成了本地版本,可是距離作業真正的完成還有關鍵的一步,那就是服務器的搭載,完成了人生中第一次購買服務器並搭載項目的成就。忙活了一個上下午,服務器運行以后還有頻發的突發情況,意外的bug之類種種,解決下來屬實頭大,好在最后甚至在有盈余的情況下完成了,還可以教舍友配置服務器(其實不難就是走了太多彎路)。可以說是收獲滿滿了,不過說實在對於我以后從事游戲崗的作用就沒有那么大了,平台類項目終究是個框架,現在網上輪子很多,想水的話很容易就可以做出一個縫合怪,認真從底層寫起,懂得怎么團隊協作,才是我從這次作業中得到的最大收獲。
221801114
在看到作業要求的時候,我心里是很慌張的,因為自己本身並沒有什么項目經驗,不知道前后端的工作區分、如何進行對接等等。好在有隊友的幫助,他負責前端,我負責后端,我可以使用自己熟悉的Java編寫后端數據庫連接部分、數據處理和一些Servlet。在嘗試編寫了部分功能以及功能成功對接以后,漸漸進入了狀態,更有信心完成剩下的功能。好的隊友也很重要,能夠一起學習、一起進步,隊友也給我提供了很多的幫助,讓我能狗順利的完成這一次的作業。相對於以前的個人編程,從這一次的結對作業中,我對合作編程有了更好的經驗與理解,學會了怎么更好的和他人溝通編程。
八、評價結對隊友
對221801135的評價
很有想法,思路很清晰,知道自己想要做的是什么、想要做成什么樣子。技術也過硬,雖然負責前端部分的設計,但是如果我后端出現了問題,他也能夠為我提供幫助,給人一種能夠很安心的感覺。
對221801114的評價
善於溝通,勤奮刻苦,可以很快的對新的改動提出合理意見,並且能夠虛心接受建議。認真負責,對於任務一絲不苟,會及時匯報進度,並且幫助完成一些額外工作,不會抱怨辛苦,任勞任怨。