團隊名稱: 雲打印
做業要求: 團隊做業第六次—團隊Github實戰訓練
做業目標:搭建一個相對公平公正的抽獎系統,根據QQ聊天記錄,完成從統計參與抽獎人員頒佈抽獎結果的基本流程。
Github地址:Github地址css
隊員學號 | 隊員姓名 | 我的博客地址 | 備註 |
---|---|---|---|
221600412 | 陳宇 | http://www.cnblogs.com/chenyuu/ | 隊長 |
221600411 | 陳迎仁 | https://www.cnblogs.com/yinen/ | |
221600409 | 蔡森林 | https://www.cnblogs.com/csl8013/ | |
221600401 | 陳詩嫺 | https://www.cnblogs.com/orangepoem/ | |
221600408 | 蔡鴻鍵 | https://www.cnblogs.com/jichiwoyaochi/ |
隊員學號 | 隊員姓名 | 這次做業任務 | 貢獻比例 |
---|---|---|---|
221600412 | 陳宇 | 項目管理、後端代碼的編寫,服務器的部署 | 23% |
221600411 | 陳迎仁 | 後端邏輯模塊的編寫,聊天記錄過濾的處理,博客文檔的編寫 | 21% |
221600401 | 陳詩嫺 | 編寫博客文檔結構,前端美工設計 | 15% |
221600409 | 蔡森林 | 附加功能的實現,數據處理與挖掘、編寫附加功能部分的博客文檔 | 21% |
221600408 | 蔡鴻鍵 | 前端代碼的編寫與設計 | 20% |
本項目爲web項目,搭載在阿里雲服務器上,web訪問連接爲: 項目運行地址html
前端:前端
開發工具爲PhpStorm,開發語言爲Ajax、js、css、HTML,框架爲boostrap;運行環境爲各種瀏覽器(谷歌瀏覽器、火狐瀏覽器、IE6以上的IE瀏覽器等)java
後端python
開發工具爲IntelliJ IDEA Ultimate,開發語言爲java,框架爲boostrap;運行環境爲java環境git
***github
@RequestMapping("/draw") public ResponseData draw(String email,String name, String document, Integer winnerNum, String startTime, String endTime, String resultTime, String keyWord, Integer filterType, String award, HttpServletRequest request) throws ClientException, IOException, MessagingException { ResponseData responseData = new ResponseData(); LotteryDrawRule lotteryDrawRule=new LotteryDrawRule(LotteryDrawFilter.getFilterTypeString(filterType),keyWord,startTime, endTime,resultTime, winnerNum); //LotteryDrawFilter lotteryDrawFilter=new LotteryDrawFilter(lotteryDrawRule,"/home/QQrecord-2022.txt"); LotteryDrawFilter lotteryDrawFilter=new LotteryDrawFilter(lotteryDrawRule,"G:\\MyJavaWeb\\Luckydraw\\src\\main\\resources\\QQrecord-2022.txt"); Map<String, Integer> users = lotteryDrawFilter.doFilter(); List<User> awardUsers = LcgRandom.getResult(users,winnerNum); String str[] = award.split("\\,"); int j = 0; for(String s : str){ String awardName = s.split(":")[0]; Integer awardNum = Integer.valueOf(s.split(":")[1]); for (int i = 0; i<awardNum;i++){ if(j<awardUsers.size()){ awardUsers.get(j).setAward(awardName); j++; } } } StringBuilder awardString = new StringBuilder(); for (User u: awardUsers) { awardString.append(u.toString()+"\r\n"); } awardString.append(" "); DrawLuckResult dr = new DrawLuckResult(name,document,keyWord,startTime,endTime,resultTime,winnerNum,award, LotteryDrawFilter.getFilterTypeString(filterType),awardString.toString()); drawLuckResultDao.insert(dr); responseData.setData(dr); Thread t = new Thread(new Runnable() { @Override public void run() { try { StringBuilder sb = new StringBuilder(); for (User u: awardUsers) { sb.append(u.toString()+"</br>"); } sb.append(" "); EmailUtil.send465("中獎結果","<h1>中獎結果通知</h1></br>" + sb.toString(),email); } catch (MessagingException e) { e.printStackTrace(); } for (User u: awardUsers) { try { String name = u.getName(); String email = null; if(name.contains("(")){ email = name.substring(name.indexOf("(") +1 ,name.lastIndexOf(")")); if (!email.contains("@qq.com")){ email += "@qq.com"; } }else if(name.contains("<")){ email = name.substring(name.indexOf("<") +1 ,name.lastIndexOf(">")); } // System.out.println(email); // 爲了避免打擾其餘人只通知本身 if (email.contains("947205926")){ EmailUtil.send465("中獎通知","恭喜" + u.getName() + "得到" + u.getAward(),"947205926@qq.com"); } } catch (Exception e) { e.printStackTrace(); } } } }); t.start(); return responseData; }
public static List<User> getResult(Map<String, Integer> users, Integer awardNum) { Iterator it = users.entrySet().iterator(); List<User> listUser = new ArrayList<>(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String name = (String) entry.getKey(); Integer weight = (Integer) entry.getValue(); // System.out.println(name + " " + weight); listUser.add(new User(name, weight)); } Random random = new Random(); // 對全部參與的用戶進行隨機排序 Collections.sort(listUser, new Comparator<User>() { @Override public int compare(User o1, User o2) { return random.nextInt(2) - 1; } }); int i = 0; int size = listUser.size(); LcgRandom lcg = new LcgRandom(); List<User> awardList = new ArrayList<>(); if (size > 0) { while (i < awardNum) { int ran = lcg.nextInt(size); // 對水羣的用戶下降獲獎權重 if (listUser.get(ran).getWeight() > 0) { listUser.get(ran).setWeight(listUser.get(ran).getWeight() - 1); } else { awardList.add(listUser.get(ran)); listUser.remove(ran); size = listUser.size(); i++; } } } return awardList; }
LCG(linear congruential generator)線性同餘算法,是一個古老的產生隨機數的算法。由如下參數組成:
| 參數 | m|a | c|X|
|:---- |:---|:----- |----- |----- |
|性質 |模數 |乘數 |加數 |隨機數 |
|做用 |取模 |移位 |偏移 |做爲結果 |
LCG算法是以下的一個遞推公式,每下一個隨機數是當前隨機數向左移動 log2 a 位,加上一個 c,最後對 m 取餘,使隨機數限制在 0 ~ m-1 內web
2、僞隨機數算法正則表達式
僞隨機數產生的方法有個逼格挺高的名字---僞隨機數發生器。僞隨機數產生器中最最最基礎的思想是均勻分佈(固然這不是惟一的思路)。通常來講,只敢說"通常來講",由於我也不敢百分百確定,現在主流的編程語言中使用的隨機數函數基本採用這種均勻分佈思想,而其中最經常使用的算法就是"線性同餘法"。算法
1. 什麼是線性同餘法?
線性同餘法基於以下線性同餘方程組
用於產生均勻型僞隨機數的線性同餘產生器(與上面的方程符號沒有對應關係)
其中,a爲"乘數",b爲"增量",m爲"模數",x0爲"種子數"。
若是產生的是區間實在(0,1)之間的,則只須要每一個數都除以m便可,即取
2. 線性同餘法產生均勻型僞隨機數須要注意什麼?
2.1)種子數是在計算時隨機給出的。好比C語言中用srand(time(NULL))函數進行隨機數種子初始化。
2.2)決定僞隨機數質量的是其他的三個參數,即a,b,m決定生成僞隨機數的質量(質量指的是僞隨機數序列的週期性)
2.3)通常b不爲0。若是b爲零,線性同餘法變成了乘同餘法,也是最經常使用的均勻型僞隨機數發生器。
3. 高性能線性同餘法參數取值要求?
3.1)通常選取方法:乘數a知足a=4p+1;增量b知足b=2q+1。其中p,q爲正整數。 PS:不要問我爲何,我只是搬運工,沒有深刻研究過這個問題。
3.2)m值得話最好是選擇大的,由於m值直接影響僞隨機數序列的週期長短。記得Java中是取得32位2進制數吧。
3.3)a和b的值越大,產生的僞隨機數越均勻
3.4)a和m若是互質,產生隨機數效果比不互質好。
反正這圖我沒有發現明顯的規律。所以這種僞隨機數在必定條件下是能夠知足隨機性性質的。
基本過濾:
首先經過正則表達式進行聊天記錄的切割,分爲用戶信息和用戶聊天內容;經過用戶信息獲取用戶的ID(暱稱+帳號);根據用戶ID的開頭進行判斷是不是系統消息、助教、教師,對這三類的對象進行過濾,不參與後續的抽獎活動,實現基本過濾。
if (Pattern.matches("系統消息\\([0-9]+\\)", userID) || Pattern.matches("教師_.*\\(.*\\)", userID)|| Pattern.matches("助教_.*\\(.*\\)", userID)) { userID = null; }
不過濾
針對抽獎名單的過濾,只實現基本過濾,並不對名單進一步的進行篩選,即只去除系統消息、教師、助教這三類對象。
普經過濾
針對抽獎名單的過濾,首先實現基本過濾,去除系統消息、教師、助教這三類用戶;其次針對只發表抽獎關鍵字的對象,也進行過濾。主要經過去除聊天記錄中的關鍵字後,若是爲空,則這條消息對應的說話人則不加入待抽獎名單。
深度過濾
針對抽獎名單的過濾,首先實現基本過濾,去除系統消息、教師、助教這三類用戶;其次針對只發表抽獎關鍵字的對象,也進行過濾;而且對於聊天內容只有圖片和抽獎關鍵字的對象也進行必定抽獎機率的下降,但不進行過濾。
BufferedReader bufferedReader = openFile(); //讀取文件 String talkContent = null; String temp = null; while ((temp = bufferedReader.readLine()) != null) { if (textType.equals("USER_TALK_CONTENT")) { if (!(isUserInfo(temp))) { talkContent += temp; } else{ //判斷髮言是否有抽獎關鍵字 if (hasKeyWord(lotteryDrawRule.getKeyWord(), talkContent) && userID != null) { talkContentFilter(talkContent); } talkContent = null; userID = null; textType = "USER_INFO"; } } if (textType.equals("USER_INFO")) { userID = getUser(temp); if (userID != null) { //去除系統消息、教師、助教 if (Pattern.matches("系統消息\\([0-9]+\\)", userID) || Pattern.matches("教師_.*\\(.*\\)", userID) || Pattern.matches("助教_.*\\(.*\\)", userID)) { userID = null; } } textType = "USER_TALK_CONTENT"; } } //測試 for (String key : users.keySet()) { System.out.println(key + ":" + users.get(key)); } read.close(); return users;
* 過濾函數 filterType=NO_FILTER:表示不過濾;全部人蔘與抽獎 filterType=NORMAL_FILTER:表示普經過濾;過濾只有抽獎關鍵字的用戶 filterType=DEEP_FILTER:表示深度過濾;過濾只有抽獎關鍵字的用戶或下降只有圖片+抽獎關鍵字的用戶的獲獎機率 */ public void talkContentFilter(String talkContent) { boolean flag = true; //判斷其需不須要被過濾 int deepNum = 0; //知足深度過濾的次數 //若是爲NO_FILTER;不執行任何過濾 if ((lotteryDrawRule.getFilterType().equals("NO_FILTER"))) { } else if((lotteryDrawRule.getFilterType().equals("NORMAL_FILTER"))){ //去除抽獎關鍵關鍵字 talkContent = talkContent.substring(talkContent.lastIndexOf('#')+1); //符合NORMAL_FILTER if (talkContent == null || talkContent.equals("")) { flag = false; } } else{ //去除抽獎關鍵關鍵字 talkContent = talkContent.substring(talkContent.lastIndexOf('#')+1); //符合NORMAL_FILTER if (talkContent == null || talkContent.equals("")) { flag = false; } //符合DEEP_FILTER if (talkContent.equals("[圖片]") && lotteryDrawRule.getFilterType().equals("DEEP_FILTER")) { deepNum = 1; } } if (!users.containsKey(userID) && flag) { // 若是該用戶id未出現過且不須要過濾 users.put(userID, deepNum); // 存入map } else if (deepNum > 0) { //若是該用戶知足深度過濾的要求,就保存他的言論次數,用於計算機率時下降它的獲獎權值 deepNum = (int) users.get(userID) + 1; users.put(userID, deepNum); } }
用戶各時間段發言統計
總體分析用戶各時間段的發言狀況,統計每一個用戶各個時間段的發言次數,而後以柱形圖形式展示,經過柱狀圖咱們很容易得出用戶在哪些時間段發言頻率較高。
def get_time(self): times = re.findall(r'\d{2}:\d{2}:\d{2}', self.data)#提取用戶發言時間哪小時 Xi = [time.split(":")[0] for time in times] sns.countplot(Xi, order=[str(i) for i in range(0, 24)]) plt.plot() plt.rcParams['font.sans-serif'] = ['SimHei'] plt.title("各時間段發言統計") plt.xlabel("時間00:00—24:00") plt.ylabel("發言次數/次") plt.savefig(r"img\hour.png", format='png') plt.close()
活躍用戶的發言狀況
局部分析前五名活躍用戶的發言狀況,統計每一個用戶的發言次數,而後進行排序提取前五個活躍用戶的發言狀況,而後以折線圖的形式展示,經過折線圖咱們很容易發現這五個用戶在哪些時間段發言頻率較高。
def get_active(self): str_list = re.findall(r'\d{2}:\d{2}:\d{2} .*?\n', self.data) chat = {} i = 0 for string in str_list:#提取用戶暱稱及其發言的時間段分佈 size = len(string) - 1 dict2 = {} if string[9:size] != "系統消息(10000)": if not chat.__contains__(string[9:size]): i = i + 1 dict2[string[0:2]] = 1 chat[string[9:size]] = dict2 else: if not chat[string[9:size]].__contains__(string[0:2]): chat[string[9:size]][string[0:2]] = 1 else: chat[string[9:size]][string[0:2]] = chat[string[9:size]][string[0:2]] + 1 dict3 = {} for key, dic in chat.items():#降序排序統計用戶活躍狀況 count = 0 for val in dic.values(): count += val dict3[key] = count result = dict(sorted(dict3.items(), key=operator.itemgetter(1), reverse=True)) colors = ['red', 'green', 'blue', 'orange', 'black'] Xi = [str(k) for k in range(0, 24)] i = 0 for key in result.keys():#遍歷前五名活躍用戶的發言狀況 if i >= 5: break Yi = [] for j in range(0, 24): Yi.append(0) for key2 in chat[key].keys(): Yi[int(key2)] = chat[key][key2] plt.plot(Xi, Yi, color=colors[i], label=key) i = i + 1 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.xticks(range(len(Xi))) plt.legend() plt.title("活躍用戶統計") plt.xlabel("時間00:00—24:00") plt.ylabel("發言次數/次") plt.savefig(r"img\active.png", format='png') plt.close()
分析聊天記錄有效關鍵詞
對用戶聊天記錄進行有效關鍵詞提取與分析,而後對這些關鍵詞進行整合分析,繪出詞雲圖,經過詞雲圖咱們很容易得出,聊天記錄中哪些關鍵詞使用頻率較高。
def get_wordcloud(self): pattern = re.compile(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} .*?\(\d+\)\n(.*?)\n', re.DOTALL) contents = re.findall(pattern, self.data) word_list = [] for sentence in contents: sentence = sentence.replace("[表情]", "").replace("[圖片]", "").replace("@全體成員", "") if sentence != "" and not sentence.__contains__("撤回了一條消息") and not sentence.__contains__("加入本羣。") and \ not sentence.__contains__('長按複製此消息,打開最新版支付寶就能領取!') and not sentence.__contains__('請使用新版手機QQ查收紅.'): word_list.append(" ".join(jieba.cut(sentence.strip()))) new_text = " ".join(word_list) wordcloud = WordCloud(background_color="white", width=1200, height=1000, min_font_size=50, font_path="simhei.ttf", random_state=50, ) my_wordcloud = wordcloud.generate(new_text) plt.imshow(my_wordcloud) plt.axis("off") wordcloud.to_file(r'img\wordcloud.png')
實驗結果以下
PlusA.txt聊天記錄數據分析
PlusB.txt聊天記錄數據分析
一、思路分析
經過接受服務器傳來的獲獎名單json數據,而後對json字符串進行相應的處理,提取出獲獎者的暱稱,QQ號或郵箱和獎品名稱,而後對相應的模塊進行繪畫,生成海報圖。
header = '[雲打印抽獎] QQ互動' title = '2019年4月QQ互動獲獎名單' chapter = ['暱稱', 'QQ號', '獎品'] string = '恭喜以上獲獎的同窗,咱們將在近期發出本次活動的獎勵,請有獲獎的同窗注意關注本平臺抽獎動態,感謝您的參與,謝謝!' n = 19 foot = [string[i:i + n] for i in range(0, len(string), n)] # 設置字體和顏色 font_type = r'font\my_font.ttc' header_font = ImageFont.truetype(font_type, 40) title_font = ImageFont.truetype(font_type, 23) chapter_font = ImageFont.truetype(font_type, 25) email_font = ImageFont.truetype(font_type, 18) list_font = ImageFont.truetype(font_type, 24) foot_font = ImageFont.truetype(font_type, 20) header_color = '#FFFFFF' title_color = '#EE0000' chapter_color = '#CD3333' list_color = '#EE2C2C' foot_color = '#EE3B3B' # 設置圖片 img = 'img/mode.png' new_img = 'img/scholarship.png' image = Image.open(img) draw = ImageDraw.Draw(image) width, height = image.size # header header_x = 38 header_y = 880 draw.text((header_x, height - header_y), u'%s' % header, header_color, header_font) # title title_x = header_x + 30 title_y = header_y - 140 draw.text((title_x, height - title_y), u'%s' % title, title_color, title_font) # chapter chapter_x = title_x - 20 chapter_y = title_y - 40 draw.text((chapter_x, height - chapter_y), u'%s' % chapter[0], chapter_color, chapter_font) draw.text((chapter_x + 140, height - chapter_y), u'%s' % chapter[1], chapter_color, chapter_font) draw.text((chapter_x + 270, height - chapter_y), u'%s' % chapter[2], chapter_color, chapter_font) # 獲取student_list data = sys.argv[1] contents = data.split('\\r\\n') student_list = [] size = len(contents) - 1 for i in range(0, size): item = [] if contents[i].__contains__('):'): nick_name = re.findall(r'(.*?)\(', contents[i]) elif contents[i].__contains__('>:'): nick_name = re.findall(r'(.*?)<', contents[i]) if contents[i].__contains__('):'): qq = re.findall(r'\((.*?)\)', contents[i]) elif contents[i].__contains__('>:'): qq = re.findall(r'<(.*?)>', contents[i]) reward = re.findall(r':(.*?),', contents[i]) item.append(nick_name[0]) item.append(qq[0]) item.append(reward[0]) student_list.append(item) list_x = chapter_x - 20 list_y = chapter_y - 40 for student in student_list: for i in range(0, len(student)): if student[i].__contains__('@'): draw.text((list_x + i * 140, height - list_y), u'%s' % student[i], list_color, email_font) else: draw.text((list_x + i * 140, height - list_y), u'%s' % student[i], list_color, list_font) list_y = list_y - 40 #footer foot_x = chapter_x - 30 foot_y = list_y - 40 for i in range(0, len(foot)): foot_y = foot_y - 40 draw.text((foot_x, height - foot_y), u'%s' % foot[i], foot_color, foot_font) draw.text((chapter_x + 30, height - (foot_y - 40)), u'%s(雲打印)' % time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), foot_color, foot_font) image.save(new_img, 'png')
二、實驗結果以下
增長了對中獎學生的郵件提醒,經過處理聊天記錄中的用戶信息,獲取用戶的郵箱信息好比qq郵箱,實現對中獎用戶的郵件提醒,提升用戶的體驗。
public class EmailUtil { // 發件人 帳號和密碼 public static final String MY_EMAIL_ACCOUNT = "cy947205926@163.com"; public static final String MY_EMAIL_PASSWORD = "**********";// 密碼,是你本身的設置的受權碼 public static void send465(String subject,String content,String receiveEmail) throws AddressException, MessagingException { Properties p = new Properties(); p.put("mail.smtp.ssl.enable", true); p.setProperty("mail.smtp.host", MEAIL_163_SMTP_HOST); p.setProperty("mail.smtp.port", "465"); p.setProperty("mail.smtp.socketFactory.port", SMTP_163_PORT); p.setProperty("mail.smtp.auth", "true"); p.setProperty("mail.smtp.socketFactory.class", "SSL_FACTORY"); Session session = Session.getInstance(p, new Authenticator() { // 設置認證帳戶信息 @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(MY_EMAIL_ACCOUNT, MY_EMAIL_PASSWORD); } }); session.setDebug(true); MimeMessage message = new MimeMessage(session); // 發件人 message.setFrom(new InternetAddress(MY_EMAIL_ACCOUNT)); // 收件人和抄送人 message.setRecipients(Message.RecipientType.TO, receiveEmail); // 內容(這個內容還不能亂寫,有可能會被SMTP拒絕掉;多試幾回吧) message.setSubject(subject); message.setContent("<h1>"+ content +"</h1>", "text/html;charset=UTF-8"); message.setSentDate(new Date()); message.saveChanges(); Transport.send(message); } }
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | 20 | 20 |
Estimate | 估計這個任務須要多少時間 | 10 | 10 |
Development | 開發 | 200 | 320 |
Analysis | 需求分析 (包括學習新技術) | 60 | 120 |
Design Spec | 生成設計文檔 | 0 | 0 |
Design Review | 設計複審 | 0 | 0 |
Coding Standard | 代碼規範(爲目前的開發制定合適的規範) | 20 | 30 |
Design | 具體設計 | 0 | 0 |
Coding | 具體編碼 | 160 | 210 |
Code Review | 代碼複審 | 20 | 60 |
Test | 測試(自我測試,修改代碼,提交修改) | 30 | 120 |
Reporting | 報告 | 30 | 40 |
Test Repor | 測試報告 | 0 | 0 |
Size Measurement | 計算工做量 | 10 | 10 |
Postmortem & Process Improvement Plan | 過後總結, 並提出過程改進計劃 | 15 | 20 |
合計 | 575 | 960 |
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | 10 | 10 |
Estimate | 估計這個任務須要多少時間 | 10 | 10 |
Development | 開發 | 160 | 220 |
Analysis | 需求分析 (包括學習新技術) | 60 | 150 |
Design Spec | 生成設計文檔 | 0 | 0 |
Design Review | 設計複審 | 0 | 0 |
Coding Standard | 代碼規範(爲目前的開發制定合適的規範) | 5 | 5 |
Design | 具體設計 | 0 | 0 |
Coding | 具體編碼 | 120 | 160 |
Code Review | 代碼複審 | 30 | 80 |
Test | 測試(自我測試,修改代碼,提交修改) | 40 | 140 |
Reporting | 報告 | 40 | 120 |
Test Repor | 測試報告 | 0 | 0 |
Size Measurement | 計算工做量 | 10 | 10 |
Postmortem & Process Improvement Plan | 過後總結, 並提出過程改進計劃 | 15 | 20 |
合計 | 500 | 925 |
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | 5 | 10 |
Estimate | 估計這個任務須要多少時間 | 0 | 0 |
Development | 開發 | 60 | 60 |
Analysis | 需求分析 (包括學習新技術) | 20 | 40 |
Design Spec | 生成設計文檔 | 0 | 0 |
Design Review | 設計複審 | 0 | 0 |
Coding Standard | 代碼規範(爲目前的開發制定合適的規範) | 0 | 0 |
Design | 具體設計 | 60 | 60 |
Coding | 具體編碼 | 60 | 60 |
Code Review | 代碼複審 | 10 | 10 |
Test | 測試(自我測試,修改代碼,提交修改) | 10 | 15 |
Reporting | 報告 | 10 | 15 |
Test Repor | 測試報告 | 0 | 0 |
Size Measurement | 計算工做量 | 0 | 0 |
Postmortem & Process Improvement Plan | 過後總結, 並提出過程改進計劃 | 10 | 10 |
合計 | 245 | 280 |
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | 30 | 60 |
Estimate | 估計這個任務須要多少時間 | 150 | 300 |
Development | 開發 | 120 | 300 |
Analysis | 需求分析 (包括學習新技術) | 40 | 100 |
Design Spec | 生成設計文檔 | 20 | 40 |
Design Review | 設計複審 | 30 | 100 |
Coding Standard | 代碼規範(爲目前的開發制定合適的規範) | 10 | 20 |
Design | 具體設計 | 40 | 80 |
Coding | 具體編碼 | 120 | 300 |
Code Review | 代碼複審 | 30 | 150 |
Test | 測試(自我測試,修改代碼,提交修改) | 30 | 90 |
Reporting | 報告 | 20 | 20 |
Test Repor | 測試報告 | 20 | 10 |
Size Measurement | 計算工做量 | 15 | 30 |
Postmortem & Process Improvement Plan | 過後總結, 並提出過程改進計 | 775 | 1640 |
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | 30 | 30 |
Estimate | 估計這個任務須要多少時間 | 120 | 300 |
Development | 開發 | 120 | 300 |
Analysis | 需求分析 (包括學習新技術) | 40 | 80 |
Design Spec | 生成設計文檔 | 20 | 20 |
Design Review | 設計複審 | 20 | 120 |
Coding Standard | 代碼規範(爲目前的開發制定合適的規範) | 10 | 20 |
Design | 具體設計 | 30 | 60 |
Coding | 具體編碼 | 120 | 300 |
Code Review | 代碼複審 | 30 | 120 |
Test | 測試(自我測試,修改代碼,提交修改) | 30 | 60 |
Reporting | 報告 | 20 | 20 |
Test Repor | 測試報告 | 20 | 10 |
Size Measurement | 計算工做量 | 15 | 30 |
Postmortem & Process Improvement Plan | 過後總結, 並提出過程改進計劃 | 20 | 40 |
合計 | 645 | 1510 |