結對第二次作業
這個作業屬於哪個課程 | 2021春軟件工程實踐|S班 |
---|---|
這個作業要求在哪里 | 結對第二次作業——頂會熱詞統計的實現 |
結對學號 | 221801121、221801131 |
這個作業的目標 | 對已爬取的論文列表進行操作、分析已爬取到的論文信息 |
其他參考文獻 | 博客園,CSDN,菜鳥教程 |
Github地址
Github倉庫地址:https://github.com/hh-afk/PairProject
PSP表格
221801121的PSP2.1 | Personal Software Process Stages | 預估耗時(分鍾) | 實際耗時(分鍾) |
---|---|---|---|
Planning | 計划 | ||
•Estimate | 估計這個任務需要多少時間 | 20 | 20 |
Development | 開發 | ||
•Analysis | 需求分析 (包括學習新技術) | 200 | 300 |
•Design Spec | 生成設計文檔 | 20 | 20 |
•Design Review | 設計復審 | 30 | 30 |
•Coding Standard | 代碼規范 (為目前的開發制定合適的規范) | 30 | 30 |
•Design | 具體設計 | 60 | 100 |
•Coding | 具體編碼 | 800 | 700 |
•Code Review | 代碼復審 | 30 | 10 |
•Test | 測試(自我測試,修改代碼,提交修改) | 180 | 320 |
Report | 報告 | ||
•Test Report | 測試報告 | 0 | 0 |
•Size Measurement | 計算工作量 | 10 | 10 |
•Postmortem & Process Improvement Plan | 事后總結, 並提出過程改進計划 | 30 | 30 |
合計 | 1410 | 1570 |
221801131的PSP2.1 | Personal Software Process Stages | 預估耗時(分鍾) | 實際耗時(分鍾) |
---|---|---|---|
Planning | 計划 | ||
•Estimate | 估計這個任務需要多少時間 | 35 | 25 |
Development | 開發 | ||
•Analysis | 需求分析 (包括學習新技術) | 250 | 300 |
•Design Spec | 生成設計文檔 | 20 | 20 |
•Design Review | 設計復審 | 30 | 30 |
•Coding Standard | 代碼規范 (為目前的開發制定合適的規范) | 0 | 0 |
•Design | 具體設計 | 150 | 200 |
•Coding | 具體編碼 | 1000 | 1200 |
•Code Review | 代碼復審 | 30 | 20 |
•Test | 測試(自我測試,修改代碼,提交修改) | 15 | 20 |
Report | 報告 | ||
•Test Report | 測試報告 | 0 | 0 |
•Size Measurement | 計算工作量 | 10 | 10 |
•Postmortem & Process Improvement Plan | 事后總結, 並提出過程改進計划 | 30 | 30 |
合計 | 1345 | 1855 |
訪問鏈接
訪問鏈接:http://116.62.208.168:8080/demo_war
展示成品
1、主頁
2、點擊論文查詢,進入查詢界面
如果沒有輸入內容就進行搜索,則輸出所有在數據庫內的論文,有輸入則進行模糊搜索
點擊刪除可以刪除論文
點擊關鍵詞可以跳轉具體的某個關鍵詞的搜索結果
點擊摘要可以具體展示摘要內容
論文分析提供了一個詞雲圖,鼠標懸停可以顯示具體出現次數,可以通過點擊查看詳情
熱詞分析提供了3個頂會的動態條形圖
結對討論過程描述
最開始,拿到論文數據后我們不知道如何處理,我們希望處理json然后將數據提取進數據庫中,然后使用c++和python對數據進行了處理,提取到了服務器端的數據庫中,然后對論文分析、論文查詢、論文列表分別編程實現
設計實現過程
設計工具和技術
開發工具:IDEA、VSCODE、VS2019
使用技術:jsp、servlet、javabean、Echarts、Javascript、html+css
項目設計與實現
最開始我們使用c++和python處理提取了數據后,希望使用JavaEE的三件套—JSP+Servlet+JavaBean實現項目
爬蟲方面因為沒有思路所以沒有實現,數據存儲在了mysql中,通過服務器遠程訪問。
用到的圖表包括動態圖表和詞雲圖都是使用Echarts制作的,代碼內嵌進jsp中。
功能結構圖
代碼說明
- 主頁
主頁到各個頁面的跳轉直接采用了a標簽跳轉,點擊將直接跳轉。
- 搜索頁面
我們采用了get方法來傳遞參數
<form action="postList" method="get">
<img src="./img/chalunwen.png" id="imgsearch"><br>
<input type="text" name="search" id="search"/>
<input type="submit" id="buttonsub"/><br>
</form>
在servlet接受參數,首先檢查當前是翻頁還是搜索,如果是翻頁則posts中存在數據直接讀出;如果是搜索則posts為null,進入if語句。
為了減少代碼冗余,我們將搜索結果和論文管理設在同一個jsp中,如果搜索內容為空則展示論文管理界面,顯示所有內容,否則展示搜索結果。
List<Post> posts = (List<Post>) req.getAttribute("posts");// 取得共享里面的數據
if (posts == null){
String search = req.getParameter("search");
req.setAttribute("search",search);
postDAO = new PostDAOimpl();
if(search.equals(""))posts = postDAO.list();
else posts = postDAO.listSearch(search);
}
- 搜索結果&論文管理
關於分頁:采用了從網上查找的一種比較簡單的方式,即設置參數,連接跳轉。我們最后將每一頁的內容調整為30篇。
// 接收分頁頁面傳遞過來的頁面數
String strNum = req.getParameter("pageNum");
int pageNum = 0;// 表示當前要顯示的頁面數
int maxPage = 0;// 最大頁
int pageCount = posts.size();// 得到查詢出來的所有數據的數目
// 如果是第一次執行,就會接收不到數據
// 計算出要分多少頁
if (pageCount % 31 == 0) {
maxPage = pageCount / 31;
} else {
maxPage = pageCount / 31 + 1;
}
if ((strNum == null)||(strNum.equals(""))) {
strNum = "0";
} else {// 接收到了用戶點擊的第幾(pageNum)頁
pageNum = Integer.parseInt(strNum);
if (pageNum < 0 || pageNum > maxPage)pageNum = 0;
}
req.setAttribute("maxPage", maxPage);// 存儲最大頁數
req.setAttribute("pageNum", pageNum);// 將當前頁面存儲起來,給分頁頁面使用
標題:檢查搜索參數,若為空顯示論文管理,不為空則顯示***的搜索結果
<%
String flag = request.getParameter("search");
if (flag.equals("")){
%>
<img src="./img/lunwenguanli.png" width="25%">
<%
}
else {
%>
<h1><%=flag%>的搜索結果</h1>
<%
}
%>
搜索結果循環打印,展示為表格形式:按頁碼參數獲取應該展示的內容
給關鍵詞加上了跳轉,點擊則以該關鍵詞為參數搜索。
原文鏈接點擊則會按原文鏈接跳轉。
刪除數據則則利用onclick執行,點擊會有提示。
<%
if (flag != null){
int pageNum = (int) request.getAttribute("pageNum");
int maxPage = (int) request.getAttribute("maxPage");
int end = (pageNum + 1) * 31 - 1;
int begin = pageNum * 31;
List<Post> posts = (List<Post>) request.getAttribute("posts");
for(int i = begin;i < end && i < posts.size();i++)
{
Post post = (Post)posts.get(i);
%>
<tr >
<td><%=post.getTitle() %></td>
<td class="td1"><%=post.getAbs() %></td>
<td>
<%
List<String> kwd = post.getKwds();
for (int j = 0;j<kwd.size();j++){
%>
<a href="postList?search=<%=kwd.get(j)%>"><%= kwd.get(j)%>
</a>
<%
}
%>
</td>
<td><a href="<%=post.getLink()%>"><%=post.getLink() %></a></td>
<td><%=post.getYear() %></td>
<td><%=post.getType() %></td>
<td><input type="button" style="" value="刪除數據" onclick="if(confirm('確認?')==false)return false;location.href='DeleteServlet?id=<%=post.getId()%>&search=<%=flag%>'" /></td>
</tr>
<%
}
%>
由於簡介過長,我們將其多余部分隱藏,點擊之后利用layer展示
$(function () {
$("td").on("click",function() {
if (this.offsetWidth < this.scrollWidth) {
var that = this;
var text = $(this).text();
//layer.alert(text);
layer.open({
title: '內容簡介',
offset: '25%',
content: text
});
}
});
})
- 分析界面
分析界面主要利用了echarts的動圖展示和詞雲,進行了更改和加入wordcount統計的熱詞數據,代碼這里就不多放了。
關於熱詞走勢:
本來是想將3個頂會的熱詞走勢放在一個界面之中,但是失敗了,就放在了3個界面,利用下拉菜單跳轉。
詞雲的跳轉也是利用單詞作為參數跳轉搜索:
chart.on('click',function(params){
var name = params.data.name;
window.location.href="postList?search="+name;
//alert(name);
console.log(name);
});
關於詞雲的顏色:隨機生成,有時可能不太好看...
color: function () {
return 'rgb(' + [
Math.round(Math.random() * 255),
Math.round(Math.random() * 255),
Math.round(Math.random() * 255),
0.8
].join(',') + ')';
}
- 關於數據存儲
我們將助教爬取的json文件利用python解析后存入數據庫中
(剛開始部分使用c++提取,結果關於json解析的庫配置出問題用不了,然后用了一堆if強行讀取。后來又出了不知道的錯,就改用python了)
部分python代碼:
利用這個函數讀取目錄下所有文件名:
def readname():
filePath = 'E:\\ECCV'
name = os.listdir(filePath)
return name
然后存為一系列insert語句:
if __name__ == "__main__":
name = readname()
for i in name:
with open('E:\\ECCV'+i, 'r', encoding='utf-8') as f:
data = json.load(f)
value_ca = ((data['論文名稱'],data['摘要'],data['關鍵詞'], data['原文鏈接']))
fo = open("2018.txt", "a" ,encoding='utf-8')
fo.write("insert into post values (\""+data['論文名稱']+"\""+","+"\""+data['摘要']+"\""+","+"\""+
",".join(str(i) for i in data['關鍵詞'])+"\""+","+"\""+data['原文鏈接']+"\""+","+"\"2018\""+","+"\"ECCV\");\n")
不過由於這與本次作業相關不大,也就沒有保存,上面這是最后讀取的ECCV2018年的代碼。
下面是我們的數據表結構:由一個自增的字段作為主鍵
- 關於數據讀取與操作
我們操作在PostDAOIml之中。
關於查詢:我們利用了sql的%對論文名稱和關鍵詞進行模糊查詢,並且按論文年份和頂會降序排列,最近也就是2020年的論文會排在最前面。
關鍵詞:數據庫中存的是以逗號分隔的字符串,到這里則分為一個list
public List<Post> listSearch(String search) {
List<Post> postList = new ArrayList<>();
try (Connection c = DBUtil.getConnection(); Statement s = c.createStatement()) {
String sql = "select * from post where title like \'%%" + search +"%%\'" + "or keywords like \'%%" + search +"%%\'order by year desc,type";
ResultSet rs = s.executeQuery(sql);
while (rs.next()){
int id = rs.getInt("id");
String title = rs.getString("title");
String keywords = rs.getString("keywords");
String abs = rs.getString("abstract");
String link = rs.getString("link");
String year = rs.getString("year");
String type = rs.getString("type");
List<String> kwds= Arrays.asList(keywords.split(","));
postList.add(new Post(id,title,kwds,abs,link,year,type));
}
}catch (SQLException e) {
e.printStackTrace();
}
return postList;
}
還有一個獲取全部論文的List無參函數:
public List<Post> list() {
List<Post> postList = new ArrayList<>();
try (Connection c = DBUtil.getConnection(); Statement s = c.createStatement()) {
String sql = "select * from post order by year desc,type";
ResultSet rs = s.executeQuery(sql);
while (rs.next()){
int id = rs.getInt("id");
String title = rs.getString("title");
String keywords = rs.getString("keywords");
String abs = rs.getString("abstract");
String link = rs.getString("link");
String year = rs.getString("year");
String type = rs.getString("type");
List<String> kwds= Arrays.asList(keywords.split(","));
postList.add(new Post(id,title,kwds,abs,link,year,type));
}
}catch (SQLException e) {
e.printStackTrace();
}
return postList;
}
關於刪除:利用主鍵id進行刪除
public void delete(int id) {
try (Connection c = DBUtil.getConnection(); Statement s = c.createStatement()) {
String sql = "delete from post where id = '" + id + "'";
s.executeUpdate(sql);
} catch (SQLException e) {
e.printStackTrace();
}
}
刪除的參數id由上個頁面傳遞,刪除成功后則會以先前的搜索參數,回到搜索結果的列表。(如果是管理列表也同樣回到管理列表):
int id = Integer.parseInt(req.getParameter("id"));
String search = req.getParameter("search");
postDAO.delete(id);
req.getRequestDispatcher("/postList?search="+search).forward(req, resp);
- 類結構
Post類:
public class Post {
private int id;
private String title;
private List<String> kwds;
private String abs;
private String link;
private String year;
private String type;
以及構造函數和各個get和set函數。
- 其他
關於熱詞的統計:我們將數據中的熱詞讀出,存成txt,再利用經過修改的wordcount統計:
public static void kwd(){
List<String> kwd = new ArrayList<String>();
try (Connection c = getConnection(); Statement s = c.createStatement()) {
String sql = "select keywords from post where type = 'ECCV'";
ResultSet rs = s.executeQuery(sql);
while (rs.next()){
String keywords = rs.getString("keywords");
kwd.add(keywords);
}
}catch (SQLException e) {
e.printStackTrace();
}
try {
BufferedWriter out = new BufferedWriter(new FileWriter("ECCV.txt"));
for (int i=0;i<kwd.size();i++){
out.write(kwd.get(i));
out.write(',');
}
out.close();
System.out.println("文件創建成功!");
} catch (IOException e) {
}
}
我們在熱詞之間利用逗號隔開,稍微修改了下上次作業的wordcount,得到了熱詞的頻數數據。
心路歷程和收獲&評價結對隊友
221801121
- 心路歷程和收獲
看到這個題目的時候,感覺特別復雜,完全不知道如何實現,好在我的隊友f哥十分優秀,把整個比較大的項目分成了提取數據、前后端分別實現圖表和后台的數據處理,一步一步實現就簡單了許多。
總的來說項目還是要腳踏實地的分模塊一個一個做的,要是看到需求就被嚇到了然后草草做個界面交差就不會獲得能力的提高,通過這次作業學到了很多前端的知識,並且上手了echarts這么一個挺厲害的圖表庫,還進行了第一次服務器配置和部署。
-
評價隊友
隊友是個非常努力的人,結對中的后端部分全是他完成的。態度十分積極,要求發布的當天就開始討論實現一些功能了,隊友願意學習新技術、在結對中編程能力也有所提升,是一個非常棒的隊友!他的開發進度很快,給了我很多的幫助。
221801131
-
心路歷程和收獲
初見題目的時候是感覺頭很大,但是靜下心來想一想,基礎功能也不是非常難以實現,將json文件讀取分析插入數據庫后,實現的思路就簡單清晰很多了。
在進行json讀取分析的時候發現python和c++簡直不是一個難度的。。。。python解析處理json數據非常輕松+愉快,后悔沒有早上手python。在處理數據的時候還碰到的一個比較頭疼的問題是文件中有一些的特殊字符,一堆除了復制打都打不出來的字符,一插入數據庫就報錯,花了好久才完成數據的處理操作。
最后想來還是有點可惜,我們兩個人的項目經驗都不足,一開始因為JvavaEE的配置都處理一大堆問題,像是lib,tomcat版本,數據庫版本等等問題,浪費了一堆時間。
還有因為團隊git實訓,也給我們帶來了提升,對這個項目有積極影響。 -
評價隊友
隊友是個干勁十足的人,學習新技術上手很快,在結對作業中對我還是有很多幫助的,像是動態圖表和詞雲那一塊的工作都是他搞定的,部署服務器也是他教會我的。