团队名称: 云打印
做业要求: 团队做业第六次—团队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 |