团队做业第六次—团队Github实战训练

团队做业第六次—团队Github实战训练

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

github 的提交日志截图






程序运行截图





程序运行环境

  • 本项目为web项目,搭载在阿里云服务器上,web访问连接为: 项目运行地址html

  • 前端:前端

    开发工具为PhpStorm,开发语言为Ajax、js、css、HTML,框架为boostrap;运行环境为各种浏览器(谷歌浏览器、火狐浏览器、IE6以上的IE浏览器等)java

  • 后端python

    开发工具为IntelliJ IDEA Ultimate,开发语言为java,框架为boostrap;运行环境为java环境git


GUI界面







***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;
    }
  • Lgc随机数
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算法

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);
        }
    }

附加功能实现

附加需求功能

1、聊天记录数据分析

用户各时间段发言统计

总体分析用户各时间段的发言状况,统计每一个用户各个时间段的发言次数,而后以柱形图形式展示,经过柱状图咱们很容易得出用户在哪些时间段发言频率较高。

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聊天记录数据分析


2、绘画获奖名单海报

一、思路分析
经过接受服务器传来的获奖名单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);
    }
}


遇到的困难及解决方法

  • 陈宇
    • 遇到的困难:java和python对接后端的时候出现了一些问题,部署服务器的时候遇到了一些浪费了比较多的时间。java调用shell命令出了点问题
    • 解决办法:查询相关资料。团队成员一块儿努力一块儿解决了。

  • 陈迎仁
    • 遇到的困难:
      • 正则表达式应用不熟悉,在对于文档的切割过程当中,常常遇到一些切割错误;花费了较多的时间在对聊天记录的切割。
    • 解决办法:
      • 经过查询百度文档,学习郑重地表达式的基本语法,根据具体格式去寻找正则表达式的书写;与团队队友进行商量。

  • 陈诗娴
    • 遇到的困难:
      • 编程方面比较弱,算法单一,有不少不懂的
    • 解决办法:
      • 作力所能及的部分

  • 蔡森林
    • 遇到的困难:对于数据分析,本身从未接触过,不知道怎么使用python绘制图表;对先后端交互不是很懂;bug调试不是很熟练。
    • 解决办法:经过网上搜索有关python绘图知识,边模仿边践行,一步一步实现聊天记录的可视化图表,如柱形图,折线图,词云图和海报图;对于先后端的交互,主要经过接口实现,以json数据格式进行数据处理;慢慢积累调bug经验,培养独立解决问题的能力。

  • 蔡鸿键
    • 遇到的困难:
      • git bash使用不便,先后端数据传输不顺,JS参数传输遇到困难
    • 解决办法:
      • 依靠队友指导,你们一块儿测试寻找BUG,使用搜索引擎查询,多实践


马后炮

  • 陈宇

    • 若是我能力能在强一点,那么队友就能轻松一点

  • 陈迎仁

    • 若是我学java后端的进度能更快一点,那么此次我就能独自实践Java后端开发以及服务器的搭建了,实现我所渴望的技术。

  • 陈诗娴

    • 若是个人代码能力能强一点,那么就能为团队作更多事。

  • 蔡森林

    • 若是我能提早好好学python,那么我将能为个人团队作得更多

  • 蔡鸿键

    • 若是好好学习,那么就没什么BUG要解决了


PSP表格

陈宇

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
相关文章
相关标签/搜索