<file_sep>#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Created by changjie.fan on 2016/6/28
"""
将RabbitMQ队列中需要发的邮件发出去
"""

import time
import simplejson as json
import os

import smtplib
# 多个MIME对象的集合
from email.mime.multipart import MIMEMultipart
# MIME文本对象
from email.mime.text import MIMEText
from email.header import Header

import pika


def send_mail(to_list=[], sub='', content=''):
    """"send_mail("<EMAIL>","sub","content")
    :param to_list:发给谁
    :param sub:主题
    :param content:内容
    """

    # 设置服务器、用户名、口令及邮箱的后缀
    mail_host = '192.168.10.8'
    mail_user = '<EMAIL>'
    mail_pass = '<PASSWORD>'
    sender = "<EMAIL>"
    msg_root = MIMEMultipart()
    # 解决中文乱码
    att_text = MIMEText(u'%s' % content, 'html', 'UTF-8')
    # 添加邮件正文
    msg_root.attach(att_text)

    # 发件人
    msg_root['from'] = sender
    # 邮件标题
    msg_root['Subject'] = Header(sub, 'utf-8')
    # 设置时间
    msg_root['Date'] = time.ctime(time.time())
    msg_root['To'] = ';'.join(to_list)

    # 构造附件(附件路径出现中文，会提示编码错误)
    # if appendix_file:
    #     for rar_file_path in appendix_file:
    #         att = MIMEText(open(rar_file_path, 'rb').read(), 'gbk', 'UTF-8')
    #         att['Content-Type'] = 'application/octet-stream;name=%s' % Header(rar_file_path, 'UTF-8')
    #         att['Content-Disposition'] = 'attachment;filename=%s' % os.path.basename(rar_file_path)
    #         msg_root.attach(att)

    # 群发邮件
    smtp = smtplib.SMTP(mail_host, 25)
    smtp.login(mail_user, mail_pass)

    smtp.sendmail(sender, to_list, msg_root.as_string())
    # 休眠5秒
    time.sleep(5)
    # 断开与服务器的连接
    smtp.quit()


def send_mail_consumer():
    """
    发送邮件的消费者
    :return:
    """
    # 建立连接connection到localhost
    con = pika.BlockingConnection(pika.ConnectionParameters('192.168.10.207'))
    # 创建虚拟连接channel
    cha = con.channel()
    # 创建队列anheng
    result = cha.queue_declare(queue='send_email', durable=True)
    # 创建名为yanfa,类型为fanout的交换机，其他类型还有direct和topic
    cha.exchange_declare(durable=False,
                         exchange='tms',
                         type='direct', )
    # 绑定exchange和queue,result.method.queue获取的是队列名称
    cha.queue_bind(exchange='tms',
                   queue=result.method.queue,
                   routing_key='', )
    # 公平分发，使每个consumer在同一时间最多处理一个message，收到ack前，不会分配新的message
    cha.basic_qos(prefetch_count=1)
    print ' [*] Waiting for messages. To exit press CTRL+C'

    # 定义回调函数
    def callback(ch, method, properties, body):
        print " [x] Received %r" % (body,)
        message = json.loads(body)
        print send_mail([tmp for tmp in message['to_list'].split(';') if tmp], message['sub'], message['content'])
        ch.basic_ack(delivery_tag=method.delivery_tag)

    cha.basic_consume(callback,
                      queue='send_email',
                      no_ack=False, )

    cha.start_consuming()


if __name__ == '__main__':
    """
    系统异步发送邮件
    """
    # consumer = SendMailConsumer()
    # consumer.run()
    send_mail_consumer()
<file_sep>#!/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'changjie.fan'


"""
Git仓库实时备份
1. 从RabbitMQ中取出Push记录的数据库Id
2. 判断仓库在数据库中是否存在，存在的获取其在备份服务器上的路径，不存在的将其加入数据库中
2. 判断仓库在备份服务器上是否存在，不存在的即克隆，存在即更新
"""

import sys
import os
import time
import commands
import logging
from datetime import datetime
import smtplib
# 多个MIME对象的集合
from email.mime.multipart import MIMEMultipart
# MIME文本对象
from email.mime.text import MIMEText
from email.header import Header
from MySQLdb import connect, MySQLError
import amqp

reload(sys)
sys.setdefaultencoding('utf-8')


def send_mail(to_list=[], sub='', content='', appendix_file=''):
    """"send_mail("<EMAIL>","sub","content")
    :param to_list:发给谁
    :param sub:主题
    :param content:内容
    :param appendix_file: 附件
    """

    # 设置服务器、用户名、口令及邮箱的后缀
    mail_host = '192.168.10.8'
    mail_user = '<EMAIL>'
    mail_pass = '<PASSWORD>'
    sender = "<EMAIL>"
    msg_root = MIMEMultipart()
    # 解决中文乱码
    att_text = MIMEText(u'%s' % content, 'html', 'UTF-8')
    # 添加邮件正文
    msg_root.attach(att_text)

    # 发件人
    msg_root['from'] = sender
    # 邮件标题
    msg_root['Subject'] = Header(sub, 'utf-8')
    # 设置时间
    msg_root['Date'] = time.ctime(time.time())
    msg_root['To'] = ';'.join(to_list)

    # 构造附件(附件路径出现中文，会提示编码错误)
    if appendix_file != '':
        rar_file_path = appendix_file
        att = MIMEText(open(rar_file_path, 'rb').read(), 'gbk', 'UTF-8')
        att['Content-Type'] = 'application/octet-stream;name=%s' % Header(rar_file_path, 'UTF-8')
        att['Content-Disposition'] = 'attachment;filename=%s' % Header(rar_file_path, 'UTF-8')
        msg_root.attach(att)

    # 群发邮件
    smtp = smtplib.SMTP(mail_host, 25)
    smtp.login(mail_user, mail_pass)

    smtp.sendmail(sender, to_list, msg_root.as_string())
    # 休眠5秒
    time.sleep(5)
    # 断开与服务器的连接
    smtp.quit()


def getLogger():
    log_file = os.path.join('/var/log/git_backup', 'qm_'+datetime.now().strftime('%Y-%m-%d')+'.log')
    logging.basicConfig(
        level=logging.INFO,
        format='',
        datefmt='%Y-%m-%d/%H:%M:%S',
        filename=log_file,
        filemode='a'
        )

    file_handler = logging.FileHandler(log_file, 'a')
    formatter = logging.Formatter('%(asctime)s %(name)s[line:%(lineno)d]:%(levelname)s %(message)s')
    file_handler.setFormatter(formatter)

    console_handler = logging.StreamHandler()
    console_handler.setFormatter(formatter)
    logging.root.addHandler(console_handler)
    logging.root.addHandler(file_handler)
    logger = logging.getLogger('Git')
    logger.info('demo')
    return logger


class Consumer:
    """
    实时备份Git仓库
    """

    def run(self):
        conn = amqp.Connection(host='172.16.20.162:5672', userid='admin', password='<PASSWORD>', virtual_host='/', insist=False)
        chan = conn.channel()
        # durable=True持久化的队列 exclusive=True时表示是私有独立，只能创建者连接 auto_delete=Fals最后一个消费者断开也不删除队列
        chan.queue_declare(queue='Git_backup', durable=True, exclusive=False, auto_delete=False)
        chan.exchange_declare(exchange='sorting_room', type='direct', durable=True, auto_delete=False)
        chan.queue_bind(queue='Git_backup', exchange='sorting_room', routing_key='Git')

        chan.basic_consume(queue='Git_backup', no_ack=True, callback=self.commit_callback, consumer_tag="Git_consumer")
        while True:
            chan.wait()
        chan.basic_cancel("Git_consumer")

        chan.close()
        conn.close()

    def commit_callback(self, msg):
        logger = getLogger()
        row_id = msg.body
        logger.info('row_id:%s' % row_id)
        record = []
        conn = None
        cursor = None
        try:
            conn = connect(host='192.168.33.7', db='tms', user='tup', passwd='tup')
            cursor = conn.cursor()
        except MySQLError, e:
            logger.info(e)
            cursor.close()
            conn.close()
            send_mail(['<EMAIL>'], u'Git backup error', 'connect 192.168.31.91 db %s' % e)
            sys.exit(1)

        # 服务器配置
        server_sql = "SELECT remote_name, root_path, backup_server_path, repository_path, reference_name, " \
                     "repo_server.id, push_type FROM repo_push_history history JOIN repo_server ON " \
                     "history.server_ip=repo_server.ip_address WHERE history.id=%s" % row_id
        #logger.info("server_sql:%s" % server_sql)
        cursor.execute(server_sql)
        record = [tmp for tmp in cursor.fetchone()]

        if record:
            repo_url, root_path, backup_server_path, repository_path, reference_name, server_id, push_type = \
                record[0], record[1], record[2], record[3], record[4], record[5], record[6]
            repo_path = repository_path.replace(root_path, backup_server_path)
            if os.path.basename(repo_path) == 'gitolite-admin.git':
                update_sql = "UPDATE repo_push_history set status=1 WHERE id=%s" % row_id
                logger.info('update_sql:%s' % update_sql)
                cursor.execute(update_sql)
                conn.commit()
                return
            if os.path.exists(repo_path):
                status, info = commands.getstatusoutput('git --git-dir=%s remote' % repo_path)
                logger.info(info)
                if not status:
                    if 'origin' not in info.split():
                        if repo_url.endswith('29418'):
                            command = 'git --git-dir=%s remote add origin %s' % \
                                      (repo_path, os.path.join(repo_url, repo_path.split(backup_server_path)[1].strip('/')))
                        else:
                            command = 'git --git-dir=%s remote add origin %s:%s' % \
                                      (repo_path, repo_url, repo_path.split(backup_server_path)[1].strip('/'))
                        status, info = commands.getstatusoutput(command)
                        if status:
                            logger.info('command:%s' % command)
                            logger.info(info)
                            update_sql = "UPDATE repo_push_history SET status=-1, error_info='%s' WHERE id=%s" % \
                                         (info.replace('\\n', '').replace('\'', '#').replace('"', '#'), row_id)
                            logger.info(update_sql)
                            cursor.execute(update_sql)
                            conn.commit()
                            return
                    if push_type == 'delete_reference':
                        status = False
                    else:
                        if reference_name.find('/tags/') > 0:
                            command = 'git --git-dir=%s fetch -f origin refs/tags/*:refs/tags/*' % repo_path
                            status, info = commands.getstatusoutput(command)
                        else:
                            command = 'git --git-dir=%s fetch -f origin refs/heads/*:refs/heads/*' % repo_path
                            status, info = commands.getstatusoutput(command)
                    if status:
                        logger.info('ERROR command:%s' % command)
                        logger.info(info)
                        update_sql = "UPDATE repo_push_history SET status=-1, error_info='%s' WHERE id=%s" % \
                                     (info.replace('\\n', '').replace('\'', '#').replace('"', '#'), row_id)
                        logger.info('update_sql:%s' % update_sql)
                        cursor.execute(update_sql)
                        conn.commit()
                        return
                    else:
                        update_sql = "UPDATE repo_push_history set status=1 WHERE id=%s" % row_id
                        logger.info('update_sql:%s' % update_sql)
                        cursor.execute(update_sql)
                        conn.commit()
            else:
                current_path = os.getcwd()
                if not os.path.exists(os.path.dirname(repo_path)):
                    os.makedirs(repo_path)
                os.chdir(os.path.dirname(repo_path))
                command = 'git clone %s:%s --bare' % (repo_url, repo_path.split(backup_server_path)[1].strip('/'))
                status, info = commands.getstatusoutput(command)
                os.chdir(current_path)
                if status:
                    logger.info('command:%s' % command)
                    logger.info(info)
                    update_sql = "UPDATE repo_push_history set status=-1, error_info='%s' WHERE id=%s" % \
                                 (info.replace('\\n', '').replace('\'', '#').replace('"', '#'), row_id)
                    logger.info(update_sql)
                    cursor.execute(update_sql)
                    conn.commit()
                    return

            repo_sql = "SELECT id FROM repo_repo WHERE path='%s'" % repository_path
            cursor.execute(repo_sql)
            if not cursor.fetchone():
                folders = repository_path.split(root_path)[1].strip('/').split('/')
                platform_path = root_path
                platform_id = ''
                for name in folders:
                    platform_path = os.path.join(platform_path, name.strip())
                    platform_sql = "SELECT id, name FROM repo_platform WHERE path='%s' and server_id=%s" % \
                                   (platform_path, server_id)
                    logger.info(update_sql)
                    cursor.execute(platform_sql)
                    platform = cursor.fetchone()
                    if platform:
                        platform_id = platform[0]
                        platform_path = platform[1]
                        break
                if platform_id:
                    repo_name = repository_path.split(platform_path)[1].strip('/').split('.git')[0]
                    insert_sql = "INSERT INTO repo_repo(name, path, platform_id) VALUES('%s', '%s', '%s')" % \
                                 (repo_name, repository_path, platform_id)
                    logger.info(update_sql)
                    cursor.execute(insert_sql)
                    conn.commit()
                else:
                    send_mail(['<EMAIL>'], 'platform not exits',
                              'add new repo not exits platform in db<br>%s' % platform_path)

            update_sql = "UPDATE repo_push_history set status=1, backup_time='%s' WHERE id=%s" % \
                         (time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), row_id)
            logger.info(update_sql)
            cursor.execute(update_sql)
            conn.commit()
        conn.close()


if __name__ == '__main__':
    """
    需提供projectVersionManifest仓库工作区的路径
    """

    consumer = Consumer()
    consumer.run()
<file_sep># -*- encoding:utf-8 -*-
"""
项目及版本信息模块
"""

from tms import db


project_member = db.Table('tms_project_member', db.metadata,
                          db.Column('user_id', db.Integer, db.ForeignKey('tms_user.id')),
                          db.Column('project_id', db.Integer, db.ForeignKey('tms_project.id')))

project_followers = db.Table('tms_project_followers', db.metadata,
                             db.Column('user_id', db.Integer, db.ForeignKey('tms_user.id')),
                             db.Column('project_id', db.Integer, db.ForeignKey('tms_project.id'))
                             )


class Project(db.Model):
    """项目"""

    __tablename__ = 'tms_project'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(200), unique=True, index=True)
    # 项目在JIRA或其他任务管理工具中的KEY值或ID
    project_key = db.Column(db.String(64), index=True)
    # 1 工程项目具体的平台 2 app项目
    category = db.Column(db.String(10))
    description = db.Column(db.String(200))
    # 状态 1开发中、2 迭代发布中、3 DCC、4 已关闭、5 暂停、6 取消
    status = db.Column(db.String(20))
    # 文件全称
    apk_name = db.Column(db.String(64))
    # 应用包名
    apk_package = db.Column(db.String(300))
    # 用例jar包路径
    test_case_jar_url = db.Column(db.String(300))
    # 最新版本号
    last_version_number = db.Column(db.String(64))
    # 最近发布时间
    last_release_time = db.Column(db.DateTime)
    # 版本通知邮件notice email address
    to_email = db.Column(db.Text)
    # 版本通知邮件cc email address
    cc_email = db.Column(db.Text)

    # 版本测试通过后通知邮件列表
    test_pass_to_email = db.Column(db.Text)
    # 版本测试失败后通知邮件列表
    test_failure_to_email = db.Column(db.Text)

    # 开发负责人
    develop_leader_id = db.Column(db.Integer, db.ForeignKey('tms_user.id'))
    develop_leader = db.relationship("User", foreign_keys=[develop_leader_id],
                                     backref=db.backref('developer_leader'), order_by=id)
    # 测试负责人
    test_leader_id = db.Column(db.Integer, db.ForeignKey('tms_user.id'))
    test_leader = db.relationship("User", foreign_keys=[test_leader_id],
                                  backref=db.backref('test_leader'), order_by=id)

    # 项目关注者
    followers = db.relationship('User', secondary='tms_project_followers', backref='project_f')

    def __repr__(self):
        return u"<Project> %s" % self.name


class SpecialVersion(db.Model):
    """项目默认有ODM版本，特殊版本如Wiko专用版本， Sugar专用版本"""

    __tablename__ = 'tms_project_special_version'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))
    # myos配置的名称
    app_name = db.Column(db.String(64))
    description = db.Column(db.String(200))

    # 存储版本的ftp路径
    ftp_path = db.Column(db.String(300))
    # 存储版本的SVN路径
    svn_path = db.Column(db.String(300))
    # release 路径
    release_path = db.Column(db.String(300))

    # 源码仓库，默认所有的版本使用一个仓库的不同分支进行管理
    code_repo_id = db.Column(db.Integer, db.ForeignKey('repo_repo.id'))
    code_repo = db.relationship('Repository', foreign_keys=[code_repo_id],
                                backref=db.backref('code_repo', order_by=id))

    # 版本发布仓库，一类版一个仓库
    release_repo_id = db.Column(db.Integer, db.ForeignKey('repo_repo.id'))
    release_repo = db.relationship('Repository', foreign_keys=[release_repo_id],
                                   backref=db.backref('release_repo', order_by=id))

    # 版本所属项目
    project_id = db.Column(db.Integer, db.ForeignKey('tms_project.id'))
    project = db.relationship('Project', backref=db.backref('special_version', order_by=id))

    def __repr__(self):
        return "<SpecialVersion> %s" % '/'.join([self.project.name, self.name])


class Version(db.Model):
    """项目版本"""

    __tablename__ = 'tms_project_version'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(200))
    version_number = db.Column(db.String(64))
    # 版本分类
    # 0日构建版本、1正式版本
    category = db.Column(db.String(20))
    # 废弃、待自测、待测试、待发布、已发布
    status = db.Column(db.String(20))
    # 版本等级
    level = db.Column(db.String(20))
    # 版本完整的Ftp路径
    ftp_path = db.Column(db.String(300))
    # release 版本路径
    release_path = db.Column(db.String(300))
    # 版本SVN路径
    svn_path = db.Column(db.String(300))
    # 版本编译时间
    compile_time = db.Column(db.DateTime())
    # 版本编译者
    compile_username = db.Column(db.String(64))
    # 版本编译者邮箱地址
    compile_email = db.Column(db.String(64))
    # 版本JenkinsURL
    jenkins_url = db.Column(db.String(300))
    # 编译时Jenkins的配置
    jenkins_config = db.Column(db.Text)
    # 自动化测试配置
    auto_test_config = db.Column(db.Text)
    # 自动化测试结果
    auto_test_result = db.Column(db.Boolean, default=False)
    # 自动化测试报告
    auto_test_report = db.Column(db.Text)
    # 版本测试结果
    testing_result = db.Column(db.Boolean, default=False)
    # 版本测试报告
    testing_reporter = db.Column(db.Text)
    # 版测试时间
    testing_time = db.Column(db.DateTime)
    # 版本测试人
    test_user_id = db.Column(db.Integer, db.ForeignKey('tms_user.id'))
    test_user = db.relationship('User', foreign_keys=[test_user_id], backref=db.backref('tester', order_by=id))

    # 版本说明
    version_note = db.Column(db.Text)
    # 本次版本所有开发任务(JIRA任务) JSON格式
    tasks = db.Column(db.Text)
    # 本次版本的commits JSON格式
    commits = db.Column(db.Text)

    # 版本的SMI值
    smi = db.Column(db.Integer)
    # 版本发布时间
    release_time = db.Column(db.DateTime())
    # 版本发布人
    release_user_id = db.Column(db.Integer, db.ForeignKey('tms_user.id'))
    release_user = db.relationship('User', foreign_keys=[release_user_id], backref=db.backref('publisher', order_by=id))
    # 发布类型:迭代方法\DCC发放\特殊发放
    release_type = db.Column(db.String(50))

    # 发布说明
    release_note = db.Column(db.Text)
    # 遗留问题
    unresolved_issues = db.Column(db.Text)
    # 版本缺陷
    version_issues = db.Column(db.Text)
    # 版本附件
    documents = db.Column(db.String(300))
    # 本次版本上传到ftp的文件
    ftp_file = db.Column(db.String(400))
    # 本次需要更新到发布库的文件
    release_file = db.Column(db.Text, default='')

    # 版本适配信息
    adaptation_note = db.Column(db.String(500))
    # 导入注意事项
    project_import_note = db.Column(db.String(500))

    # 项目特殊版本，默默都是ODM版本
    special_version_id = db.Column(db.Integer, db.ForeignKey('tms_project_special_version.id'))
    special_version = db.relationship('SpecialVersion', backref=db.backref('special_version', order_by=id))

    # 版本所属项目
    project_id = db.Column(db.Integer, db.ForeignKey('tms_project.id'))
    project = db.relationship('Project', backref=db.backref('version', order_by=id))

    # 版本废弃时间
    version_discard_time = db.Column(db.DateTime)
    # 版本废弃人
    discard_user_id = db.Column(db.Integer, db.ForeignKey('tms_user.id'))
    discard_user = db.relationship('User', foreign_keys=[release_user_id], backref=db.backref('discard', order_by=id))

    def __repr__(self):
        return "<Version> %s" % self.version_number


class VersionHistory(db.Model):
    """版本的操作历史"""

    __tablename__ = 'tms_project_version_history'

    id = db.Column(db.Integer, primary_key=True)

    # 版本描述
    description = db.Column(db.String(200))
    # 版本编译时间
    compile_time = db.Column(db.DateTime())
    # 版本编译者
    compile_username = db.Column(db.String(64))
    # 版本编译者邮箱地址
    compile_email = db.Column(db.String(64))
    # 版本JenkinsURL
    jenkins_url = db.Column(db.String(300))
    # 编译时Jenkins的配置
    jenkins_config = db.Column(db.Text)
    # 自动化测试配置
    auto_test_config = db.Column(db.Text)
    # 自动化测试结果
    auto_test_result = db.Column(db.Boolean, default=False)
    # 自动化测试报告
    auto_test_report = db.Column(db.Text)
    # 版本测试结果
    testing_result = db.Column(db.Boolean, default=False)
    # 版本测试报告
    testing_reporter = db.Column(db.Text)
    # 版测试时间
    testing_time = db.Column(db.DateTime)
    # 版本测试人
    test_user_id = db.Column(db.Integer, db.ForeignKey('tms_user.id'))
    test_user = db.relationship('User', foreign_keys=[test_user_id], backref=db.backref('tester_history', order_by=id))

    # 版本自测人
    self_test_user_id = db.Column(db.Integer, db.ForeignKey('tms_user.id'))
    self_test_user = db.relationship('User', foreign_keys=[self_test_user_id], backref=db.backref('self_tester_history', order_by=id))

    # 版本自测结果
    self_test_result = db.Column(db.Boolean, default=False)
    # 版本自测时间
    self_test_time = db.Column(db.DateTime)

    # 本次版本所有开发任务(JIRA任务) JSON格式
    tasks = db.Column(db.Text)
    # 本次版本的commits JSON格式

    commits = db.Column(db.Text)

    # 版本
    version_id = db.Column(db.Integer, db.ForeignKey('tms_project_version.id'))
    version = db.relationship('Version', backref=db.backref('history', order_by=id))

    def __repr__(self):
        return "<VersionHistory> %s" % self.version.version_number


class ReleaseEmailAddress(db.Model):
    """版本发布通知邮件地址"""

    __tablename__ = 'tms_release_email_address'

    id = db.Column(db.Integer, primary_key=True)
    email_address = db.Column(db.Text)

    def __repr__(self):
        return "<ReleaseEmailAddress> %r" % 'EmailAddress'


class ProjectTestCase(db.Model):
    """项目所用的测试用例"""

    __tablename__ = 'tms_project_test_case'

    id = db.Column(db.Integer, primary_key=True)

    project_id = db.Column(db.Integer, db.ForeignKey('tms_project.id'))
    project = db.relationship('Project', backref=db.backref('test_case', order_by=id))

    test_case_id = db.Column(db.Integer, db.ForeignKey('tms_test_case.id'))
    test_case = db.relationship('TestCase', backref=db.backref('project', order_by=id))

    def __repr__(self):
        return "<ProjectTestCase> %s" % self.project.name
<file_sep>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Created by changjie.fan on 2016/7/4
"""
连接JIRA数据库
进行查询查询操作
"""

from MySQLdb import connect


class DBOption:

    def __init__(self, db_host, db_name, db_user, db_passwd):
        """
        数据连接
        :param db_host:
        :param db_name:
        :param db_user:
        :param db_passwd:
        """

        self.conn = connect(host=db_host, db=db_name, user=db_user, passwd=db_passwd, charset='utf8')
        self.cursor = self.conn.cursor()


    def fetch_one(self, sql):
        """返回第一条结果"""

        self.cursor.execute(sql)
        r = self.cursor.fetchall()
        if len(r) > 0:
            return [r[0][e] for e in range(len(r[0]))]
        return []

    def fetch_all(self, sql):
        """返回第一条结果"""

        self.cursor.execute(sql)
        r = self.cursor.fetchall()
        if len(r) > 0:
            return [[r[p][e] for e in range(len(r[p]))] for p in range(len(r))]
        return []

    def insert_or_update_one(self, sql):
        """
        插入一条记录
        """

        self.cursor.execute(sql)
        return self.conn.commit()

    def insert_all(self, sql, data):
        """
        插入多条纪录
        """

        self.cursor.executemany(sql, data)
        return self.conn.commit()

    def close(self):
        """
        关闭数据库连接
        """

        self.cursor.close()
        self.conn.close()


<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

"""
report 模块为周报管理模块
定义项目模块的蓝图
"""


from flask import Blueprint


report = Blueprint('report', __name__)
from . import views
<file_sep># -*- coding: utf-8 -*-
# Created by changjie.fan on 2016/6/12
"""
项目MyOS关联的Views
"""
import json
import os
from ConfigParser import ConfigParser, NoSectionError

from flask import render_template, redirect, url_for, request, flash, current_app, session, send_file
from flask.ext.login import login_user, logout_user, login_required, current_user
from flask.ext.mail import Mail, Message
from functools import update_wrapper

from . import project
from tms import db, mail
from androidProjectModules import *
from models import SpecialVersion, Version, Project

@project.route('/project_myos_config_page')
@login_required
def project_myos_config_page():
    """工程师项目MYOS配置页面"""

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '平台化MyOS项目', 'url': url_for('project.project_myos_config_page')})
    session['menu_path'] = ['项目管理', '平台化MyOS项目']

    return render_template('project/android/project_myos_config_page.html')


@project.route('/project_myos_config_data')
@login_required
def project_myos_config_data():
    """已导入平台化的项目"""

    start = int(request.args.get('start', 0))
    length = int(request.args.get('length', 10))
    search = request.args.get('search[value]', '')

    current_app.logger.info(search)
    if search:
        all_count = ProjectAndroid.query.filter(ProjectAndroid.myos_config_patch != '').filter(ProjectAndroid.name.like('%'+search+'%')).count()
    else:
        all_count = ProjectAndroid.query.count()
    records = []
    index = start + 1
    if search:
        all_record = ProjectAndroid.query.filter(ProjectAndroid.myos_config_patch != '').filter(ProjectAndroid.name.like('%'+search+'%')).all()
    else:
        all_record = ProjectAndroid.query.filter(ProjectAndroid.myos_config_patch != '').all()
    for record in all_record[start: start + length]:
        if record.parent:
            records.append({
                "id": record.id,
                "index": index,
                "name": '{0}_{1}'.format(record.parent.name, record.name),
                "project_key": record.project_key,
                "category": record.category,
                "parent": record.parent.name ,
                "myos_config_path": record.myos_config_patch,
            })
        else:
            records.append({
                "id": record.id,
                "index": index,
                "name": record.name,
                "project_key": record.project_key,
                "category": record.category,
                "parent": '',
                "myos_config_path": record.myos_config_patch,
            })
        index += 1

    return '{"recordsTotal": %s ,"recordsFiltered": %s,"data":%s}' % (all_count, all_count, json.dumps(records))


@project.route('/project_myos_config_detail_page/<int:project_id>', methods=['GET', 'POST'])
@login_required
def project_myos_config_detail_page(project_id):
    """项目myos应用的具体配置信息"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '项目MyOS应用配置' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:
        session['breadcrumbs'].append({'text': '项目MyOS应用配置', 'url': url_for('project.project_myos_config_detail_page', project_id=project_id)})

    configs = ProjectMyOSConfig.query.filter(ProjectMyOSConfig.project_id == project_id).all()
    project = ProjectAndroid.query.filter_by(id=project_id).first()
    other_project = ProjectAndroid.query.filter(db.and_(ProjectAndroid.myos_config_patch != '',
                                                        ProjectAndroid.id != project_id)).all()

    return render_template('/project/android/project_myos_config_detail_page.html',
                           project=project,
                           configs=configs,
                           other_project=other_project)


@project.route('/android_project_version/<int:project_id>')
@login_required
def android_project_version(project_id):
    """android项目的版本信息"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '项目版本' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:
        session['breadcrumbs'].append({'text': '项目版本', 'url': url_for('project.android_project_version', project_id=project_id)})

    project = ProjectAndroid.query.filter_by(id=project_id).first()
    versions = ProjectVersion.query.filter(ProjectVersion.project_id == project_id).order_by(ProjectVersion.compile_time.desc()).all()

    return render_template('/project/android/project_version_page.html', project=project, versions=versions)


@project.route('/project_version_myos_version_page/<int:project_version_id>', methods=['GET', 'POST'])
@login_required
def project_version_myos_version_page(project_version_id):
    """项目myos应用的具体配置信息"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '项目版本MyOS应用配置' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:
        session['breadcrumbs'].append({'text': '项目版本MyOS应用配置',
                                       'url': url_for('project.project_version_myos_version_page',
                                                      project_version_id=project_version_id)})

    project_version = ProjectVersion.query.filter_by(id=project_version_id).first()
    configs = ProjectVersionMyOSConfig.query.filter(ProjectVersionMyOSConfig.project_version_id == project_version_id).all()

    return render_template('/project/android/project_version_myos_config_detail_page.html', project_version=project_version, configs=configs)


@project.route('/project_myos_app_search/<string:app_name>')
@login_required
def project_myos_app_search(app_name):
    """查找APP被哪些项目及哪些版本使用了"""

    # 配置了此app且开关是打开的的项目
    project_app = ProjectMyOSConfig.query.filter(db.and_(ProjectMyOSConfig.app_name.like('%{0}%'.format(app_name)),
                                                         ProjectMyOSConfig.support == 'yes')).all()

    return render_template('/project/android/project_app_search_page.html', project_app=project_app)


@project.route('/app_project_version_page/<int:project_id>/<string:app_name>')
@login_required
def app_project_version_page(project_id, app_name):
    """项目中指定app在哪些版本里的版本信息"""


    search_sql = "SELECT conf.id, conf.current_version, conf.last_version, conf.overrides, " \
                 "ver.version_number, ver.compile_time, pro.name FROM " \
                 "project_version_myos_app_config AS conf JOIN project_version AS ver ON " \
                 "conf.project_version_id=ver.id JOIN project_android AS pro ON ver.project_id= pro.id WHERE " \
                 "pro.id={0} AND conf.app_name='{1}'".format(project_id, app_name)

    current_app.logger.info(search_sql)
    records = db.session.execute(search_sql).fetchall()
    result = []
    for record in records:
        result.append({
            'id': record[0],
            'current_version': record[1],
            'last_version': record[2],
            'overrides': record[3],
            'version_number': record[4],
            'compile_time': record[5]
        })

    return render_template('/project/android/project_app_version_page.html',
                           app_name=app_name,
                           project_version_app=result)


@project.route('/project_myos_app_edit/<int:project_id>/<int:project_app_id>', methods=['GET', 'POST'])
@project.route('/project_myos_app_edit/<int:project_id>', defaults={'project_app_id':None}, methods=['GET', 'POST'])
@login_required
def project_myos_app_edit(project_id, project_app_id):
    """
    编辑项目的MYOS应用配置
    :param project_id:
    :param project_app_id:
    :return:
    """

    project_obj = ProjectAndroid.query.filter(ProjectAndroid.id == project_id).first()
    if project_app_id:
        project_app = ProjectMyOSConfig.query.filter(ProjectMyOSConfig.id == project_app_id).first()
        versions = [tmp.version_number for tmp in Version.query.join(SpecialVersion).filter(
            db.and_(SpecialVersion.app_name == project_app.app_name)).order_by(Version.id.desc()).all()]
    else:
        project_app = ProjectMyOSConfig()
        versions = []

    all_app = SpecialVersion.query.all()

    if request.method == 'POST':
        app_name = request.form.get('app_name')

        other = ProjectMyOSConfig.query.filter(db.and_(ProjectMyOSConfig.project_id == project_id,
                                                       ProjectMyOSConfig.app_name == app_name)).first()
        if other and other.id != project_app.id:
            flash('%s:应用配置重复了' % app_name, 'warning')
        else:
            project_app.app_name = app_name
            if request.form.get('support') == 'on':
                project_app.support = 'yes'
            else:
                project_app.support = 'no'
            project_app.overrides = request.form.get('overrides')
            project_app.app_version = request.form.get('version')
            project_app.project = project_obj

            db.session.add(project_app)
            db.session.commit()
            return redirect(url_for('project.project_myos_config_detail_page', project_id=project_id))

    return render_template('/project/android/project_myos_app_edit_page.html',
                           all_app=all_app,
                           project=project_obj,
                           app=project_app,
                           versions=versions)



@project.route('/get_myos_app_verion')
@login_required
def get_myos_app_verion():
    """
    获取myos 应用所有已发布的版本
    :return:
    """

    app_name = request.args.get('app_name')

    versions = [{'name': tmp.version_number}for tmp in Version.query.join(SpecialVersion).filter(
        db.and_(SpecialVersion.app_name== app_name)).order_by(Version.id.desc()).all()]

    return json.dumps(versions)


@project.route('/import_project_myos_config/<int:project_id>', methods=['GET', 'POST'])
@login_required
def import_project_myos_config(project_id):
    """
    导入项目的myos配置文件
    :param project_id:
    :return:
    """

    # 保存配置文件
    new_config_file = request.files["app_config"]
    current_app.logger.info(new_config_file)
    upload_path = os.path.join(current_app.config['TMS_TEMP_DIR'], current_user.username, 'ApkVersion.ini')
    if not os.path.exists(os.path.dirname(upload_path)):
        os.makedirs(os.path.dirname(upload_path))
    new_config_file.save(upload_path)

    project_apk = []
    cf = ConfigParser()
    try:
        cf.read(upload_path)
        for section in cf.sections():
            if section == 'ProjectInfo':
                continue
            project_apk.append(section)

    except NoSectionError, e:
        print e
        print '[ERROR] apkVersion file config error'

    # 项目当前的配置
    current_config = ProjectMyOSConfig.query.filter(ProjectMyOSConfig.project_id == project_id).all()
    if current_config:
        for app in current_config:
            if app.app_name in project_apk:
                app.support = cf.get(app.app_name, 'support')
                app.app_version = cf.get(app.app_name, 'version')
                if cf.has_option(app.app_name, 'overrides'):
                    app.overrides = cf.get(app.app_name, 'overrides')
                db.session.add(app)
                project_apk.remove(app.app_name)
            else:
                db.session.delete(app)

        for app_name in project_apk:
            project_config = ProjectMyOSConfig()
            project_config.app_name = app_name
            project_config.support = cf.get(app_name, 'support')
            project_config.app_version = cf.get(app_name, 'version')
            if cf.has_option(app_name, 'overrides'):
                project_config.overrides = cf.get(app_name, 'overrides')
            project_config.project_id = project_id

            db.session.add(project_config)
        db.session.commit()
    else:
        for app_name in project_apk:
            project_config = ProjectMyOSConfig()
            project_config.app_name = app_name
            project_config.support = cf.get(app_name, 'support')
            project_config.app_version = cf.get(app_name, 'version')
            if cf.has_option(app_name, 'overrides'):
                project_config.overrides = cf.get(app_name, 'overrides')
            project_config.project_id = project_id

            db.session.add(project_config)
        db.session.commit()

    return redirect(url_for('project.project_myos_config_detail_page', project_id=project_id))


@project.route('/export_project_myos_config/<int:project_id>', methods=['GET', 'POST'])
@login_required
def export_project_myos_config(project_id):
    """
    项目myos配置从数据库中导出到ApkVersion.ini文件
    :param project_id:
    :return:
    """
    project_obj = ProjectAndroid.query.filter(ProjectAndroid.id == project_id).first()
    current_config = ProjectMyOSConfig.query.filter(ProjectMyOSConfig.project_id == project_id).all()

    cf = ConfigParser()
    for app in current_config:
        cf.add_section(app.app_name)
        cf.set(app.app_name, 'support', app.support)
        cf.set(app.app_name, 'app_version', app.app_version)
        if app.overrides:
            cf.set(app.app_name, 'overrides', app.overrides)

    upload_path = os.path.join(current_app.config['TMS_TEMP_DIR'], current_user.username, 'ApkVersion.ini')
    if not os.path.exists(os.path.dirname(upload_path)):
        os.makedirs(os.path.dirname(upload_path))
    fp = open(upload_path, 'w')
    fp.write('[ProjectInfo]\n')
    fp.write('#android version,optional value:L/M/N\n')
    fp.write('ANDROID_VERSION=%s\n\n' % project_obj.android_version)
    fp.write('#chip platform,optional value:MTK/QCOM/RDA\n')
    fp.write('PLATFORM=%s\n\n' % project_obj.platform_name)
    cf.write(fp)
    fp.close()

    return send_file(open(upload_path, 'rb'), as_attachment=True, attachment_filename=os.path.basename(upload_path))


@project.route('/project_myos_delete', methods=['GET', 'POST'])
@login_required
def project_myos_delete():
    """
    删除项目应用配置
    :return:
    """

    app_ids = request.form.get('app_ids')
    app_config = ProjectMyOSConfig.query.filter(ProjectMyOSConfig.id.in_(app_ids.split(','))).all()
    for app in app_config:
        db.session.delete(app)
    db.session.commit()

    return '{"result": true}'

@project.route('/project_myos_app_info/<string:app_name>/<string:version_number>')
@login_required
def project_myos_app_info(app_name, version_number):
    """
    显示应用版本的详细信息
    :param app_name:
    :param version_number:
    :return:
    """

    app_version = Version.query.join(SpecialVersion).filter(db.and_(SpecialVersion.app_name == app_name,
                                                                    Version.version_number == version_number)).first()

    return redirect(url_for('developer.version_self_test', version_id=app_version.id))
<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""
import os
from datetime import datetime
import simplejson as json
from flask import render_template, redirect, url_for, request, flash, current_app, jsonify, g, session, send_file
from flask.ext.login import login_user, logout_user, login_required

from forms import LoginForm, ChangePasswordForm, ChangeEmailForm
from models import User, Role, SystemMenu, SystemParameter
from . import auth
from tms import db
from util import create_menu, getOrder

from tms.departmentMan.models import Department


@auth.route('/login', methods=['GET', 'POST'])
def login():
    """用户登录"""

    if request.method == 'POST':
        user = User.query.filter_by(username=request.form['username']).first()

        if user is not None and user.verify_password(request.form['password']):
            login_user(user, request.form.get('remember'))
            user.last_seen = datetime.now()
            role = user.role
            if role and role.menu:
                menu_ids = [menu_id for menu_id in role.menu.split(',') if id]
                menus = [menu for menu in SystemMenu.query.filter(
                    db.and_(SystemMenu.activate == True,
                            SystemMenu.id.in_(menu_ids))).order_by(SystemMenu.order).all()]
                current_page = role.index_page
            else:
                menus = []
                current_page = ''

            session['user'] = {
                'id': user.id,
                'username': user.username,
                'display_name': user.display_name,
                'email': user.email,
                'role_name': role.name,
				'role_id': user.role_id,
            }
            session['index_page'] = current_page
            session['menu'] = create_menu(menus)
            session['breadcrumbs'] = [{'text': 'Home', 'url': url_for(role.index_page)}]

            return redirect(url_for(current_page, url_path=current_page))

        flash('Invalid email or password.', 'danger')

    return render_template('auth/login.html')


@auth.route('/logout')
@login_required
def logout():
    """用户登出"""

    logout_user()
    session.clear()
    flash('You have been logged out.', 'info')
    return redirect(url_for('auth.login'))


@auth.route('/settings', methods=['GET', 'POST'])
@login_required
def settings():
    """账号设置设置"""
    password_form = ChangePasswordForm()
    email_form = ChangeEmailForm()

    return render_template('auth/settings.html', password_form=password_form, email_form=email_form)


@auth.route('/change_email', methods=['POST'])
@login_required
def change_email():
    data = json.loads(request.form.get('data'))
    return str(data['email'])


@auth.route('/account_page')
@login_required
def account_page():

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': u'账号管理', 'url': url_for('auth.account_page')})
    session['menu_path'] = [u'系统管理', u'账号管理']

    return render_template('admin/account/account_page.html', users=User.query.all())


@auth.route('/add_account', methods=['GET', 'POST'])
@login_required
def add_account():
    """新增账号"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '添加账号' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:
        session['breadcrumbs'].append({'text': '添加账号', 'url': url_for('auth.add_account')})

    new_user = None
    if request.method == 'POST':
        if request.form['password'] != request.form['password2']:
            flash(u'两次输入的密码不相同', 'danger')
            return redirect(url_for('auth.add_account'))

        user = User.query.filter_by(email=request.form['email']).first()
        if user is not None:
            flash(u'此邮箱地址已注册过', 'danger')
            return redirect(url_for('auth.add_account'))

        new_user = User()
        new_user.username = request.form['username']
        new_user.email = request.form['email']
        new_user.password = request.form['<PASSWORD>']
        new_user.display_name = request.form['display_name']
        new_user.role = Role.query.filter_by(id=request.form['role']).first()
        new_user.department_id = request.form['department']

        db.session.add(new_user)
        db.session.commit()

        flash(u'新增成功', 'info')

        return redirect(url_for('auth.account_page'))
    else:
        #get请求

        #一级部门
        top_departments = Department.query.filter_by(level=1,activate=1).order_by(Department.id.asc()).all()
        #二级部门
        second_departments = Department.query.filter_by(level=2,activate=1).order_by(Department.id.asc()).all()
        #三级部门
        three_departments = Department.query.filter_by(level=3,activate=1).order_by(Department.id.asc()).all()
        #四级部门
        four_departments = Department.query.filter_by(level=4,activate=1).order_by(Department.id.asc()).all()
        #五级部门
        five_departments = Department.query.filter_by(level=5,activate=1).order_by(Department.id.asc()).all()
        #六级部门
        six_departments = Department.query.filter_by(level=6,activate=1).order_by(Department.id.asc()).all()

        array = []
        array.append('一级部门')
        array.append('二级部门')
        array.append('三级部门')
        array.append('四级部门')
        array.append('五级部门')
        array.append('六级部门')

        dict = {}
        dict['一级部门'] =  top_departments
        dict['二级部门'] =  second_departments
        dict['三级部门'] =  three_departments
        dict['四级部门'] =  four_departments
        dict['五级部门'] =  five_departments
        dict['六级部门'] =  six_departments

        return render_template('admin/account/account_edit.html', user=new_user,
                           roles=Role.query.filter(db.and_(Role.name != 'translator',
                                                           Role.name != 'customer_manager')).all(),
                           current_page='auth.account_page',array=array,dict=dict,addflag=True)


@auth.route('/edit_account/<int:user_id>', methods=['GET', 'POST'])
@login_required
def edit_account(user_id):
    """编辑账号"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '编辑账号' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:
        session['breadcrumbs'].append({'text': '编辑账号', 'url': url_for('auth.edit_account', user_id=user_id)})

    user = user = User.query.filter_by(id=user_id).first()

    if request.method == 'POST':
        other_user = User.query.filter_by(email=request.form['email']).first()
        if other_user and user.id != other_user.id:
            flash(u'此邮箱地址已注册过', 'danger')
        if request.form['password'] != request.form['password2']:
            flash(u'两次输入的密码不相同', 'danger')

        if user.password_hash != request.form['password']:
            user.password = request.form['password']

        user.username = request.form['username']
        user.email = request.form['email']
        user.display_name = request.form['display_name']
        user.role = Role.query.filter_by(id=request.form['role']).first()
        user.department_id = request.form['department']

        db.session.add(user)
        db.session.commit()
        flash(u'更新成功!', 'info')

        return redirect(url_for('auth.account_page'))
    else:
        #get请求

        #一级部门
        top_departments = Department.query.filter_by(level=1,activate=1).order_by(Department.id.asc()).all()
        #二级部门
        second_departments = Department.query.filter_by(level=2,activate=1).order_by(Department.id.asc()).all()
        #三级部门
        three_departments = Department.query.filter_by(level=3,activate=1).order_by(Department.id.asc()).all()
        #四级部门
        four_departments = Department.query.filter_by(level=4,activate=1).order_by(Department.id.asc()).all()
        #五级部门
        five_departments = Department.query.filter_by(level=5,activate=1).order_by(Department.id.asc()).all()
        #六级部门
        six_departments = Department.query.filter_by(level=6,activate=1).order_by(Department.id.asc()).all()

        array = []
        array.append('一级部门')
        array.append('二级部门')
        array.append('三级部门')
        array.append('四级部门')
        array.append('五级部门')
        array.append('六级部门')

        dict = {}
        dict['一级部门'] =  top_departments
        dict['二级部门'] =  second_departments
        dict['三级部门'] =  three_departments
        dict['四级部门'] =  four_departments
        dict['五级部门'] =  five_departments
        dict['六级部门'] =  six_departments

        return render_template('admin/account/account_edit.html', user=user,
                           roles=Role.query.filter(db.and_(Role.name != 'translator', Role.name != 'customer_manager')).all(),
                           array=array,dict=dict,addflag=False)


@auth.route('/system_menu_page')
@login_required
def system_menu_page():
    """菜单清单"""

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': u'菜单管理', 'url': url_for('auth.system_menu_page')})
    session['menu_path'] = [u'系统管理', u'菜单管理']

    result = []
    menus = SystemMenu.query.order_by(SystemMenu.order).all()
    parents = []
    result = {}
    for menu in menus:
        if menu.parent and menu.parent in result.keys():
            result[menu.parent].append(menu)
        elif menu.parent:
            result.update({menu.parent:[menu]})
            parents.append(menu.parent)
        else:
            result.update({menu.parent: []})

    parents = sorted(parents, key=getOrder)
    return render_template('admin/account/system_menu_page.html', parents=parents, menus=result)


@auth.route('/add_system_menu', methods=['GET', 'POST'])
@login_required
def add_system_menu():
    """新增菜单"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '添加菜单' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:
        session['breadcrumbs'].append({'text': '添加菜单',
                                       'url': url_for('auth.add_system_menu')})
    if request.method == 'POST':
        name = request.form['name']
        action = request.form.get('action', '')
        activate = request.form.get('activate')
        note = request.form.get('note')
        order = request.form['order']
        parent_id = request.form['parent']

        other_menu = SystemMenu.query.filter(db.and_(SystemMenu.action == action,
                                                     SystemMenu.parent_id == parent_id)).first()
        if other_menu:
            flash(u'同名菜单存在', 'danger')
        else:
            menu = SystemMenu()
            menu.name = name
            menu.action = action or ''
            menu.note = note
            if activate == 'on':
                menu.activate = True
            else:
                menu.activate = False
            menu.style = ''
            menu.note = note
            menu.order = order
            if parent_id:
                menu.parent_id = parent_id
            db.session.add(menu)
            db.session.commit()

            flash(u'新增成功!', 'info')

    return render_template('admin/account/system_menu_edit.html', menu=None,
                           parent_menus=SystemMenu.query.filter(
                               db.and_(SystemMenu.action == '', SystemMenu.activate == True)).all())


@auth.route('/_menu_activate')
@login_required
def set_menu_activate():
    menu_id = request.args.get('id', 0, type=int)
    activate = request.args.get('activate')
    if activate == 'Enable':
        db.session.execute('UPDATE tms_menu SET activate=1 WHERE id =%s or parent_id=%s' % (menu_id, menu_id))
    elif activate == 'Disable':
        db.session.execute('UPDATE tms_menu SET activate=0 WHERE id =%s or parent_id=%s' % (menu_id, menu_id))
    db.session.commit()

    return '{"result": true}'


@auth.route('/edit_system_menu/<int:menu_id>',  methods=['GET', 'POST'])
@login_required
def edit_system_menu(menu_id):
    """修改菜单"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '编辑菜单' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:
        session['breadcrumbs'].append({'text': '编辑菜单',
                                       'url': url_for('auth.edit_system_menu', menu_id=menu_id)})

    menu = SystemMenu.query.filter_by(id=menu_id).first()
    if request.method == 'POST':
        name = request.form['name']
        action = request.form.get('action', '')
        activate = request.form.get('activate')
        note = request.form.get('note')
        order = request.form['order']
        parent_id = request.form['parent']

        other_menu = SystemMenu.query.filter(db.and_(SystemMenu.action == action,
                                                     SystemMenu.parent_id == parent_id)).first()
        if other_menu and other_menu.id != menu.id:
            flash(u'同名菜单存在', 'danger')
        else:
            menu.name = name
            menu.action = action or ''
            menu.note = note
            if activate == 'on':
                menu.activate = True
            else:
                menu.activate = False
            menu.style = ''
            menu.note = note
            menu.order = order
            if parent_id:
                menu.parent_id = parent_id
            db.session.add(menu)
            db.session.commit()

            flash(u'修改成功!', 'info')

    return render_template('admin/account/system_menu_edit.html', menu=menu,
                           parent_menus=SystemMenu.query.filter(
                               db.and_(SystemMenu.action == '', SystemMenu.activate == True)).all())


@auth.route('/role_page')
@login_required
def role_page():
    """系统角色管理"""

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': u'角色管理', 'url': url_for('auth.role_page')})
    session['menu_path'] = [u'权限管理', u'角色管理']

    roles = Role.query.all()
    return render_template('admin/account/role_page.html', roles=roles,
                           current_page='auth.role_page')


@auth.route('/edit_role/<int:role_id>', methods=['GET', 'POST'])
@login_required
def edit_role(role_id):

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '编辑角色' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:
        session['breadcrumbs'].append({'text': '编辑角色',
                                       'url': url_for('auth.edit_role', role_id=role_id)})

    role = Role.query.filter_by(id=role_id).first()

    if request.method == 'POST':
        all_id = [str(menu_id) for menu_id in request.form.getlist('role_menu') if menu_id]

        if all_id:
            query_sql = "SELECT DISTINCT  parent_id FROM tms_menu WHERE id IN(%s)" % ','.join(all_id)

            for row in db.engine.execute(query_sql):
                if row[0] != 'None' and row[0] != None:
                    all_id.append(str(row[0]))

            role.menu = ','.join(list(set(all_id)))
        else:
            role.menu = ''

        role.name = request.form['name']
        role.index_page = request.form['index_page']

        db.session.add(role)
        db.session.commit()

        user_ids = request.form.getlist('role_remember')
        remember = User.query.filter_by(role_id=role.id).all()
        for user in remember:
            if user.id in user_ids:
                user_ids.remove(user.id)
            else:
                user.role = None
                db.session.add(user)

        users = User.query.filter(User.id.in_(user_ids))
        for user in users:
            user.role = role
            db.session.add(user)
        db.session.commit()
        return redirect(url_for('auth.role_page'))

    menus = SystemMenu.query.filter_by(activate=True).order_by(SystemMenu.order).all()
    users = User.query.all()
    if role.menu:
        role_menu = [int(id) for id in role.menu.split(',') if id]
    else:
        role_menu = []

    return render_template('admin/account/role_edit.html',
                           role=role, menus=menus, users=users, role_menu=role_menu)


@auth.route('/add_role/', methods=['GET', 'POST'])
@login_required
def add_role():

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '添加角色' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:
        session['breadcrumbs'].append({'text': '添加角色', 'url': url_for('auth.add_role')})

    if request.method == 'POST':
        other_role = Role.query.filter_by(name=request.form['name']).first()
        if other_role:
            flash(u'同名角色已存在', 'danger')
        else:
            all_id = [str(menu_id) for menu_id in request.form.getlist('role_menu') if menu_id]

            query_sql = "SELECT DISTINCT  parent_id FROM tms_menu WHERE id IN(%s)" % ','.join(all_id)

            for row in db.engine.execute(query_sql):
                if row[0] != 'None' and row[0] != None:
                    current_app.logger.info(row[0])
                    all_id.append(str(row[0]))

            role = Role()
            role.menu = ','.join(list(set(all_id)))
            role.name = request.form['name']
            role.index_page = request.form['index_page']

            db.session.add(role)
            db.session.commit()

            users = User.query.filter(User.id.in_(request.form.getlist('role_remember')))
            for user in users:
                user.role = role
                db.session.add(user)
            db.session.commit()
            return redirect(url_for('auth.role_page'))

    menus = SystemMenu.query.filter_by(activate=True).order_by(SystemMenu.order).all()
    users = User.query.all()

    return render_template('admin/account/role_edit.html', role=None, menus=menus, users=users,
                           current_page='auth.role_page')


@auth.route('/download_file/')
@login_required
def download_file():
    """文件下载"""

    file_path = request.args.get('file_path', '')
    return send_file(
            open(file_path, 'rb'),
            as_attachment=True, attachment_filename=os.path.basename(file_path))


<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

"""任务模块的蓝图"""
from flask import Blueprint

task = Blueprint('task', __name__)
from . import views
<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

"""Git 仓库路径导入数据库中"""
import sys
import os

from MySQLdb import connect


def main(repo_dir_path):
    """将指定目录下的repo仓库路径，添加到数据库中"""

    repos = []
    for dir_name in os.listdir(repo_dir_path):
        if dir_name.endswith('.git') and os.path.isdir(os.path.join(repo_dir_path, dir_name)):
            repos.append(os.path.join(repo_dir_path, dir_name))

    print repos
    con = connect(host='192.168.33.7', db='tms', user='gms', passwd='gms', charset='utf8')
    cursor = con.cursor()

    exits_sql = "SELECT path FROM repo_repo WHERE platform_id=89"
    cursor.execute(exits_sql)
    exists_repo = [tmp[0] for tmp in cursor.fetchall()]
    print exists_repo
    new_repo = []
    for repo_path in repos:
        if repo_path not in exists_repo:
            new_repo.append((repo_path.split('/home/git/repositories/tinno/release/')[1].split('.git')[0].strip(),
                             repo_path, 89, 3))

    print new_repo
    insert_repo = "INSERT into repo_repo(name, path, platform_id, category) VALUES (%s, %s, %s, %s)"

    cursor.executemany(insert_repo, new_repo)
    con.commit()
    con.close()


def update_repo_url():

    con = connect(host='192.168.33.7', db='tms', user='gms', passwd='gms', charset='utf8')
    cursor = con.cursor()

    exits_sql = "SELECT id, name FROM repo_repo WHERE platform_id=89"
    cursor.execute(exits_sql)
    for tmp in cursor.fetchall():
        update_sql = "UPDATE repo_repo set repo_url='%s' WHERE id=%s" % ('scm:tinno/release/'+tmp[1], tmp[0])
        cursor.execute(update_sql)
        con.commit()
    con.close()


if __name__ == '__main__':
    # main(sys.argv[1])
    update_repo_url()<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

"""
将硬盘中平台信息
仓库路径导入到数据库中
"""
import os
from optparse import OptionParser

from DBOption import DBOption


def get_all_repo(platform_path):
    """
    获取平台下所有的Git仓库路径
    """

    repos = []
    for root, dirs, files in os.walk(platform_path):
        for dir_name in dirs:
            if dir_name == '.repo' or dir_name == '.git':
                continue
            git_path = os.path.join(root, dir_name)
            if git_path.find('/.repo/') > 0:
                continue
            if git_path.endswith('.git'):
                repos.append(git_path)

    return repos


def main():
    """
    1. 导入服务器上的所有平台
    2. 导入平台下的所有仓库
    """

    des = u'import platform and Repository'
    prog= u'AddRepository'
    ver = u'%prog v0.0.1'
    usage = u'%prog ip_address platform_path dir_path'

    parse = OptionParser(description=des, prog=prog, version=ver, usage=usage, add_help_option=True)

    parse.add_option('-t', '--server-ip', action='store', type='string', dest='ip_address',
                     help='repository server ip address')
    parse.add_option('-p', '--platform-name', action='store', type='string', dest='platform_name',
                     help='repository platform name')

    # 解析命令行参数
    options, args = parse.parse_args()
    ip_address = options.ip_address
    platform_name = options.platform_name.strip('/')

    # mysql
    db_url = {'host': '192.168.31.91', 'db_name': 'tms', 'username': 'root', 'password': '<PASSWORD>'}
    # sqlite
    #db_url = '/home/roy/workspace/tms/tms-dev.sqlite'

    db_option = DBOption(db_url, 'mysql')
    db_option.db_connect()
    server_sql = "SELECT id, root_path FROM repo_server WHERE ip_address='%s' " % ip_address
    server = db_option.fetch_one(server_sql)
    server_id = server[0]
    server_root_path = server[1]

    platform_path = os.path.join(server_root_path, platform_name)
    platform_sql = "SELECT id FROM repo_platform WHERE path='%s' AND server_id=%s" % (platform_path, server_id)
    platform = db_option.fetch_one(platform_sql)
    if not platform:
        platform_insert_sql = "INSERT INTO repo_platform (name, path, server_id) VALUES ('%s', '%s', %s)" % \
                              (platform_name, platform_path, server_id)
        db_option.insert_one(platform_insert_sql)
        platform = db_option.fetch_one(platform_sql)

    platform_id = platform[0]

    repo_paths = get_all_repo(platform_path)
    repo_sql = "SELECT path FROM repo_repo WHERE platform_id=%s" % platform_id
    exists_repo_paths = db_option.fetch_all(repo_sql)
    if exists_repo_paths:
        exists_repo_paths = [tmp[0] for tmp in exists_repo_paths]

    repo_values = []
    for repo_path in repo_paths:
        if repo_path in exists_repo_paths:
            continue
        repo_name = repo_path.split(platform_path)[1].split('.git')[0].strip('/')
        repo_values.append((repo_name, repo_path, platform_id))

    if repo_values:
        repo_insert_sql = "INSERT INTO repo_repo (name, path, platform_id) values (%s, %s, %s)"
        for r in range(len(repo_values)/50+1):
            db_option.insert_all(repo_insert_sql, repo_values[r*50:(r+1)*50])


if __name__ == '__main__':
    main()
<file_sep>{% extends "developer/developer_base.html" %}
{% block header %}
    {{ super() }}
    <link href="{{ url_for('static', filename='assets/global/plugins/datatables/plugins/bootstrap/dataTables.bootstrap.css') }}"
          rel="stylesheet" type="text/css"/>
{% endblock %}

{% block page_content %}
    <!-- BEGIN CONTENT -->
    <div class="row">
        <div class="col-md-12">
            <!-- BEGIN EXAMPLE TABLE PORTLET-->
            <div class="portlet box grey-cascade">
                <div class="portlet-title">
                    <div class="caption">
                        {% if all_project %}
                            <i class="fa fa-globe"></i>所有项目
                        {% else %}
                            <i class="fa fa-globe"></i>我的项目
                        {% endif %}
                    </div>
                    <div class="actions">
                        <a href="{{ url_for('developer.all_project') }}" class="btn btn-primary btn-sm">
                            显示所有项目</a>
                    </div>
                </div>
                <div class="portlet-body">
                    {% if session['user']['role_name'] == 'scm' %}
                        <div class="table-toolbar">
                            <div class="row">
                                <div class="col-md-6">
                                    <div class="btn-group">
                                        <a id="add_account_btn" class="btn green"
                                           href="{{ url_for('developer.add_project') }}">
                                            添加项目<i class="fa fa-plus"></i>
                                        </a>
                                    </div>
                                </div>
                            </div>
                        </div>
                    {% endif %}
                    <table class="table table-striped table-bordered table-hover" id="project_table">
                        <thead>
                        <tr>
                            <th width="20px;">No</th>
                            <th>项目名称</th>
                            <th>JIRA_KEY</th>
                            <th>项目类型</th>
                            <th>开发负责人</th>
                            <th>测试负责人</th>
                            <th>状态</th>
                            <th>最新版本</th>
                            <th>发布时间</th>
                            <th>Action</th>
                        </tr>
                        </thead>
                        <tbody>
                        {% for index, project, project_type in projects %}
                            <tr data="{{ project.id }}" type="">
                                <td>{{ index }}</td>
                                <td>{{ project.name }}</td>
                                <td>{{ project.project_key }}</td>
                                <td>{{ project.category }}</td>
                                <td>{{ project.develop_leader.display_name }}</td>
                                <td>{{ project.test_leader.display_name }}</td>
                                <td>{{ project.status }}</td>
                                <td>{{ project.last_version_number }}</td>
                                <td>{{ project.last_release_time }}</td>
                                <td>
                                    <a class="btn btn-xs btn-primary"
                                       href="{{ url_for('developer.project_version', project_id=project.id) }}">所有版本</a>
                                    {% if all_project %}
                                        {% if  project_type == 'other' %}
                                            <a id="as{{ project.id }}" class="btn btn-xs green" href="#"
                                               onclick="start_project({{ project.id }}, 1);return false;">关注</a>
                                            <a id="ac{{ project.id }}" class="btn btn-xs btn-warning" href="#"
                                               style="display: none;"
                                               onclick="start_project({{ project.id }}, -1);return false;">取消</a>
                                        {% elif project_type == 'start' %}
                                            <a id="as{{ project.id }}" class="btn btn-xs green" href="#"
                                               style="display: none;"
                                               onclick="start_project({{ project.id }}, 1);return false;">关注</a>
                                            <a id="ac{{ project.id }}" class="btn btn-xs btn-warning" href="#"
                                               onclick="start_project({{ project.id }}, -1);return false;">取消</a>
                                        {% endif %}
                                    {% endif %}
                                </td>
                            </tr>
                        {% endfor %}
                        {% for index, project, project_type, type in project_android %}
                            <tr data="{{ project.id }}" type="{{ type }}">
                                <td>{{ index }}</td>
                                <td>{{ project.name }}</td>
                                <td>{{ project.project_key }}</td>
                                <td>{{ project.category }}</td>
                                <td></td>
                                <td></td>
                                <td>{{ project.status }}</td>
                                <td></td>
                                <td></td>
                                <td>
                                    <a id="ah{{ project.id }}" class="btn btn-xs blue" href="{{ url_for('project.project_code_history', project_id=project.id) }}">历史</a>

                                    {% if  project_type == 'other' %}
                                        <a id="as{{ project.id }}" class="btn btn-xs green" href="#"
                                           onclick="start_android_project({{ project.id }}, 1);return false;">关注</a>
                                        <a id="ac{{ project.id }}" class="btn btn-xs btn-warning" href="#"
                                           style="display: none;"
                                           onclick="start_android_project({{ project.id }}, -1);return false;">取消</a>
                                    {% elif project_type == 'start' %}
                                        <a id="as{{ project.id }}" class="btn btn-xs green" href="#"
                                           style="display: none;"
                                           onclick="start_android_project({{ project.id }}, 1);return false;">关注</a>
                                        <a id="ac{{ project.id }}" class="btn btn-xs btn-warning" href="#"
                                           onclick="start_android_project({{ project.id }}, -1);return false;">取消</a>
                                    {% endif %}
                                </td>
                            </tr>
                        {% endfor %}
                        </tbody>
                    </table>
                </div>
            </div>
            <!-- END EXAMPLE TABLE PORTLET-->
        </div>
    </div>
    <!-- END PAGE CONTENT-->
{% endblock %}

{% block script %}
    <script src="{{ url_for('static', filename='assets/global/plugins/datatables/media/js/jquery.dataTables.min.js') }}"
            type="text/javascript"></script>
    <script src="{{ url_for('static', filename='assets/global/plugins/datatables/extensions/TableTools/js/dataTables.tableTools.min.js') }}"
            type="text/javascript"></script>
    <script src="{{ url_for('static', filename='assets/global/plugins/datatables/plugins/bootstrap/dataTables.bootstrap.js') }}"
            type="text/javascript"></script>
    <script src="{{ url_for('static', filename='assets/global/plugins/bootbox/bootbox.min.js') }}"
            type="text/javascript"></script>
    <script src="{{ url_for('static', filename='js/developer/developer_page.js') }}" type="text/javascript"></script>
    <script type="text/javascript">
        $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
    </script>
{% endblock %}

<file_sep># -*- encoding:utf-8 -*-
"""
部门管理模块
"""

from tms import db


class Department(db.Model):
    """部门"""

    __tablename__ = 'tms_department'

    id = db.Column(db.Integer, primary_key=True)
    #部门名称
    name = db.Column(db.String(100))
    #是否激活
    activate = db.Column(db.Boolean, default=True)
    #备注
    note = db.Column(db.String(100))
    #排序
    order = db.Column(db.Integer)
    #level 层级
    level = db.Column(db.Integer)

    parent_id = db.Column(db.Integer, db.ForeignKey('tms_department.id'), nullable=True)
    parent = db.relation('Department', uselist=False, remote_side=[id],
                         backref=db.backref('children', order_by=order))

    leader = db.Column(db.String(100))

    def __repr__(self):
        return '<Department %r>' % self.name
<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

"""
数据库操作
"""


class DBOption:
    def __init__(self, db_url, db_type='sqlite'):
        self.db_type = db_type
        self.db_url = db_url
        self.conn = None
        self.cursor = None

    def db_connect(self):
        """
        数据库连接、获取游标
        """
        if self.db_type == 'sqlite':
            import sqlite3
            self.conn = sqlite3.connect(self.db_url)
            self.cursor = self.conn.cursor()
        else:
            from MySQLdb import connect
            self.conn = connect(host=self.db_url['host'], db=self.db_url['db_name'], user=self.db_url['username'],
                                passwd=self.db_url['password'], charset='utf8')
            self.cursor = self.conn.cursor()

    def fetch_one(self, sql):
        """返回第一条结果"""
        print sql
        self.cursor.execute(sql)
        r = self.cursor.fetchall()
        if len(r) > 0:
            return [r[0][e]for e in range(len(r[0]))]
        return []

    def fetch_all(self, sql):
        """返回第一条结果"""

        print sql
        self.cursor.execute(sql)
        r = self.cursor.fetchall()
        if len(r) > 0:
            return [[r[p][e] for e in range(len(r[p]))] for p in range(len(r))]
        return []

    def insert_one(self, sql):
        """
        插入一条记录
        """
        print sql
        self.cursor.execute(sql)
        return self.conn.commit()

    def insert_all(self, sql, data):
        """
        插入多条纪录
        """
        print sql
        self.cursor.executemany(sql, data)
        return self.conn.commit()

    def close(self):
        """
        关闭数据库连接
        """

        self.cursor.close()
        self.conn.close()
<file_sep># -*- encoding:utf-8 -*-
"""
编译信息模块
"""

from tms import db


class CompileMode(db.Model):
    """编译模式"""

    __tablename__ = 'jenkins_compilemode'

    id = db.Column(db.Integer, primary_key=True)

    compile_mode = db.Column(db.String(20), unique=True, index=True)


    def __repr__(self):
        return u"<CompileMode> %s" % self.compile_mode


class CompileOrder(db.Model):
    """编译命令"""

    __tablename__ = 'jenkins_compileorder'

    id = db.Column(db.Integer, primary_key=True)

    compile_order = db.Column(db.String(20), unique=True, index=True)


    def __repr__(self):
        return u"<CompileOrder> %s" % self.compile_order


class CompileType(db.Model):
    """编译类型"""

    __tablename__ = 'jenkins_compiletype'

    id = db.Column(db.Integer, primary_key=True)

    compile_type = db.Column(db.String(20), unique=True, index=True)


    def __repr__(self):
        return u"<CompileType> %s" % self.compile_type


class PlatForm(db.Model):
    """jenkins--编译平台"""

    __tablename__ = 'jenkins_platform'

    id = db.Column(db.Integer, primary_key=True)

    name = db.Column(db.String(20), unique=True, index=True)


    def __repr__(self):
        return u"<PlatForm> %s" % self.name


class Module(db.Model):
    """模块编译参数"""

    __tablename__ = 'jenkins_module'

    id = db.Column(db.Integer, primary_key=True)

    pro_name = db.Column(db.String(20), index=True)

    command = db.Column(db.Text())

    path = db.Column(db.Text())

    platform_id = db.Column(db.Integer,db.ForeignKey('jenkins_platform.id'))
    platform = db.relationship("PlatForm", foreign_keys=[platform_id],
                                     backref=db.backref('platform'), order_by=id)

    module_name = db.Column(db.String(20), index=True)


    def __repr__(self):
        return u"<Module> %s" % self.module_name

<file_sep>var TableManaged = function () {

    var initTable1 = function () {

        var table = $('#project_table');
        table.dataTable({
            "language": {
                "aria": {
                    "sortAscending": ": activate to sort column ascending",
                    "sortDescending": ": activate to sort column descending"
                },
                "emptyTable": "No data available in table",
                "info": "Showing _START_ to _END_ of _TOTAL_ records",
                "infoEmpty": "No records found",
                "infoFiltered": "(filtered1 from _MAX_ total records)",
                "lengthMenu": "Show _MENU_ records",
                "search": "Search:",
                "zeroRecords": "No matching records found",
                "paginate": {
                    "previous":"Prev",
                    "next": "Next",
                    "last": "Last",
                    "first": "First"
                }
            },

            "bStateSave": true, // save datatable state(pagination, sort, etc) in cookie.

            "columns": [{
                "orderable": false
            }, {
                "orderable": true
            }, {
                "orderable": true
            }, {
                "orderable": true
            }, {
                "orderable": true
            }],
            "lengthMenu": [
                [12, 25, 50, -1],
                [12, 25, 50, "All"] // change per page values here
            ],
            // set the initial value
            "pageLength": 12,
            "pagingType": "bootstrap_full_number",
            "columnDefs": [{  // set default column settings
                'orderable': false,
                'targets': [0]
            }, {
                "searchable": false,
                "targets": [0]
            }],
            "order": [
                [2, "asc"]
            ] // set first column as a default sort by asc
        });
    }

    return {

        //main function to initiate the module
        init: function () {
            if (!jQuery().dataTable) {
                return;
            }

            initTable1();
        }

    };

}();

jQuery(document).ready(function() {
    TableManaged.init();

    $('#project_table tbody').on( 'dblclick', 'td', function () {
        var tdSeq = $(this).parent().find("td").index($(this)[0]);
        if(tdSeq == 8) {
            return;
        }else {
            $(this).toggleClass('selected');
            window.location.href = $SCRIPT_ROOT + '/qa/edit_project/' +$(this).parent().attr('data');
        }
    });
    $("#project_table tr").live({
        mouseover:function(){
            $(this).css("cursor","pointer");
        }
    });

});<file_sep># -*- coding: utf-8 -*-
# Created by changjie.fan on 2016/8/30
"""
项目共性问题
"""
import os
from datetime import datetime, date
import simplejson as json

from flask import render_template, redirect, url_for, request, flash, current_app, session, send_file
from flask.ext.login import login_user, logout_user, login_required, current_user
from flask.ext.mail import Mail, Message
from functools import update_wrapper

from jira import JIRAError

from . import project
from tms import db, mail
from tms.auth.models import User, Role
from androidProjectModules import *
from tms.auth.models import SystemParameter
from tms.baseline.models import BPlatform
from tms.utils.JiraClient import JiraClient
from tms.utils.excelOption import ExcelOptions


@project.route('/common_issue_page')
@login_required
def common_issue_page():
    """项目共性问题页面"""

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '共性问题', 'url': url_for('project.common_issue_page')})
    session['menu_path'] = ['项目管理', '共性问题']

    return render_template('project/android/common_issue/common_issue_page.html')


@project.route('/common_issue_data')
@login_required
def common_issue_data():
    """项目共性问题 测试工程师的界面"""

    start = int(request.args.get('start', 0))
    length = int(request.args.get('length', 10))
    search = request.args.get('search[value]', '')
    session['search'] = search

    if search:
        all_count = CommonIssue.query.filter(
            db.or_(CommonIssue.name.like('%'+search+'%'),
                   CommonIssue.description.like('%' + search + '%'),
                   CommonIssue.baseline_name.like('%' + search + '%'),
                   CommonIssue.platform_name.like('%' + search + '%'))).count()
        all_record = CommonIssue.query.filter(
            db.or_(CommonIssue.name.like('%'+search+'%'),
                   CommonIssue.description.like('%' + search + '%'),
                   CommonIssue.baseline_name.like('%' + search + '%'),
                   CommonIssue.platform_name.like('%' + search + '%'))).order_by(CommonIssue.submit_issue_time.desc()).all()

    else:
        all_count = CommonIssue.query.count()
        all_record = CommonIssue.query.order_by(CommonIssue.submit_issue_time.desc()).all()

    records = []
    index = start + 1
    for record in all_record[start: start + length]:
        enable_edit = False
        if record.status == '1' and record.tester_id == current_user.id:
            enable_edit = True
        if record.status == 1:
            status = '已创建'
        elif record.status == 2:
            status = '填写方案'
        elif record.status == 3:
            status = '等待确认'
        elif record.status == 4:
            status = '已确认'
        else:
            status = '已创建'

        records.append({
            "id": record.id,
            "index": index,
            "name": record.name,
            "jira_key": record.jira_key,
            "project_name": record.project.name,
            "platform_name": record.platform_name,
            "status": status,
            "enable_edit": enable_edit
        })
        index += 1

    return '{"recordsTotal": %s ,"recordsFiltered": %s,"data":%s}' % (all_count, all_count, json.dumps(records))


@project.route('/add_common_issue', methods=['GET', 'POST'])
@login_required
def add_common_issue():
    """添加项目共性问题"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '添加问题' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index + 1]
            break
    else:
        session['breadcrumbs'].append(
            {'text': '添加问题', 'url': url_for('project.add_common_issue')})

    common_issue = CommonIssue()

    all_project = [tmp for tmp in ProjectAndroid.query.order_by(ProjectAndroid.create_time.desc()).all() if tmp.status != '关闭']
    all_platform = BPlatform.query.all()
    all_developer = User.query.join(Role).filter(db.and_(User.activate==True, Role.name=='Android开发工程师')).all()
    all_spm = User.query.join(Role).filter(db.and_(User.activate==True, Role.name=='spm')).all()

    if request.method == 'POST':
        name = request.form.get('name', '').strip()
        project_id = request.form.get('project_id', '')
        description = request.form.get('description', '').strip()
        solutions = request.form.get('solutions', '').strip()
        effect_scope = ','.join(request.form.getlist('effect_scope'))
        platform_name = request.form.get('platform_name', '')
        baseline_name = request.form.get('baseline_name', '').strip()
        jira_key = request.form.get('jira_key', '').strip()
        attachment = request.form.get('attachment', '')
        developer_id = request.form.get('developer_id', '')
        spm_id = request.form.get('spm_id', '')
        category = request.form.get('category', '')
        issue_from = request.form.get('issue_from', '')

        other = CommonIssue.query.filter(CommonIssue.name == name).first()
        if other:
            flash(u'同名的共性问题已存在', 'warning')
        else:
            common_issue.name = name
            common_issue.project_id = project_id
            common_issue.description = description
            common_issue.solutions = solutions
            common_issue.effect_scope = effect_scope
            common_issue.platform_name = platform_name
            common_issue.baseline_name = baseline_name
            common_issue.tester_id = current_user.id
            if jira_key:
                common_issue.jira_key = jira_key
            if developer_id:
                common_issue.developer_id = developer_id
                common_issue.status = 2
            if spm_id:
                common_issue.spm_id = spm_id
            common_issue.submit_issue_time = datetime.now()
            common_issue.attachment = attachment
            common_issue.category = category
            common_issue.issue_from = issue_from

            db.session.add(common_issue)
            db.session.commit()

            project_obj = ProjectAndroid.query.filter_by(id=project_id).first()

            if common_issue.effect_scope == '平台':
                msg = Message(
                    u'[TSDS] %s平台 共性问题——%s' % (common_issue.platform_name, common_issue.name),
                    sender='<EMAIL>',
                    recipients=[common_issue.developer.email, '<EMAIL>']
                )
            elif common_issue.effect_scope == '产品':
                msg = Message(
                    u'[TSDS] %s产品 共性问题——%s' % (project_obj.name, common_issue.name),
                    sender='<EMAIL>',
                    recipients=[common_issue.developer.email, '<EMAIL>']
                )
            else:
                msg = Message(
                    u'[TSDS] %s%s 共性问题——%s' % (project_obj.name, project_obj.customer_area, common_issue.name),
                    sender='<EMAIL>',
                    recipients=[common_issue.developer.email, common_issue.spm.email, common_issue.tester.email,
                                '<EMAIL>']
                )
            msg.html = '<h5>项目共性问题名称:</h5>{0}<br><br>' \
                       '<h5>问题创建人:</h5>{1}<{2}><br><br>' \
                       '<h5>提出问题项目:</h5>{3}<br><br>' \
                       '<h5>项目所属平台名称:</h5>{4}<br><br>' \
                       '<h5>项目当前基线:</h5>{5}<br><br>' \
                       '<h5>问题描述:</h5>{6}<br>'.format(common_issue.name, current_user.display_name, current_user.email,
                                                 common_issue.project.name, common_issue.platform_name,
                                                 common_issue.baseline_name, common_issue.description)
            mail.send(msg)

            return redirect(url_for('project.common_issue_page'))

    issue_category_options = ['平台', '产品', '订单']
    issue_from_options = []
    issue_from_param = SystemParameter.query.filter_by(key='ISSUE_FROM').first()
    if issue_from_param:
        issue_from_options = [tmp for tmp in str(issue_from_param.value).split('\n') if tmp.strip()]
    return render_template('project/android/common_issue/common_issue_edit.html',
                           common_issue=common_issue,
                           all_project=all_project,
                           all_developer=all_developer,
                           all_spm=all_spm,
                           all_platform=all_platform,
                           issue_category_options=issue_category_options,
                           issue_from_options=issue_from_options)


@project.route('/qa_edit_common_issue/<int:common_issue_id>', methods=['GET', 'POST'])
@login_required
def qa_edit_common_issue(common_issue_id):
    """测试工程师编辑项目共性问题"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '修改问题' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index + 1]
            break
    else:
        session['breadcrumbs'].append(
            {'text': '修改问题', 'url': url_for('project.qa_edit_common_issue', common_issue_id=common_issue_id)})

    common_issue = CommonIssue.query.filter_by(id=common_issue_id).first()
    if common_issue.attachment:
        attachment = [(tmp, os.path.basename(tmp))for tmp in common_issue.attachment.split('#')]
    else:
        attachment = []

    all_project = [tmp for tmp in ProjectAndroid.query.order_by(ProjectAndroid.create_time.desc()).all() if tmp.status != '关闭']
    all_platform = BPlatform.query.all()
    all_developer = User.query.join(Role).filter(db.and_(User.activate==True, Role.name=='Android开发工程师')).all()
    all_spm = User.query.join(Role).filter(db.and_(User.activate==True, Role.name=='spm')).all()

    if request.method == 'POST':
        name = request.form.get('name', '').strip()
        description = request.form.get('description', '').strip()
        solutions = request.form.get('solutions', '').strip()
        effect_scope = ','.join(request.form.getlist('effect_scope'))
        platform_name = request.form.get('platform_name', '')
        baseline_name = request.form.get('baseline_name', '').strip()
        jira_key = request.form.get('jira_key', '').strip()
        attachment = request.form.get('attachment', '')
        developer_id = request.form.get('developer_id', '')
        spm_id = request.form.get('spm_id', '')
        category = request.form.get('category', '')
        issue_from = request.form.get('issue_from', '')

        other = CommonIssue.query.filter(CommonIssue.name == name).first()
        if other and other.id != common_issue_id:
            flash(u'同名的共性问题已存在', 'warning')
        else:
            common_issue.name = name
            common_issue.description = description
            common_issue.category = category
            common_issue.solutions = solutions
            if effect_scope != ',':
                common_issue.effect_scope = effect_scope
            common_issue.platform_name = platform_name
            common_issue.baseline_name = baseline_name
            if jira_key:
                common_issue.jira_key = jira_key
            if developer_id:
                common_issue.developer_id = developer_id
                common_issue.status = 2
            if spm_id:
                common_issue.spm_id = spm_id
            common_issue.submit_issue_time = datetime.now()
            if common_issue.attachment and common_issue.attachment.strip():
                common_issue.attachment = '#'.join([tmp.strip() for tmp in common_issue.attachment.split('#') if tmp]
                + [tmp.strip() for tmp in attachment.split('#') if tmp.strip()])
            else:
                common_issue.attachment = attachment
            common_issue.issue_from = issue_from

            db.session.add(common_issue)
            db.session.commit()
            return redirect(url_for('project.common_issue_page'))

    issue_category_options = ['平台', '产品', '订单']
    issue_from_options = []
    issue_from_param = SystemParameter.query.filter_by(key='ISSUE_FROM').first()
    if issue_from_param:
        issue_from_options = [tmp for tmp in str(issue_from_param.value).split('\n') if tmp.strip()]
    return render_template('project/android/common_issue/common_issue_edit.html',
                           common_issue=common_issue,
                           all_project=all_project,
                           all_developer=all_developer,
                           all_spm=all_spm,
                           all_platform=all_platform,
                           attachment=attachment,
                           issue_category_options=issue_category_options,
                           issue_from_options=issue_from_options)


@project.route('/dev_edit_common_issue/<int:common_issue_id>', methods=['GET', 'POST'])
@login_required
def dev_edit_common_issue(common_issue_id):
    """开发工程师编辑项目共性问题"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '修改问题' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index + 1]
            break
    else:
        session['breadcrumbs'].append(
            {'text': '修改问题', 'url': url_for('project.dev_edit_common_issue', common_issue_id=common_issue_id)})

    common_issue = CommonIssue.query.filter_by(id=common_issue_id).first()
    if common_issue.attachment:
        attachment = [(tmp, os.path.basename(tmp))for tmp in common_issue.attachment.split('#')]
    else:
        attachment = []

    all_project = [tmp for tmp in ProjectAndroid.query.order_by(ProjectAndroid.create_time.desc()).all() if tmp.status != '关闭']
    all_platform = BPlatform.query.all()
    all_developer = User.query.join(Role).filter(db.and_(User.activate==True, Role.name=='Android开发工程师')).all()
    all_spm = User.query.join(Role).filter(db.and_(User.activate==True, Role.name=='spm')).all()

    if request.method == 'POST':
        solutions = request.form.get('solutions', '').strip()
        spm_id = request.form.get('spm_id', '')
        attachment = request.form.get('attachment', '')

        common_issue.solutions = solutions
        if spm_id:
            common_issue.spm_id = spm_id
            common_issue.status = 3

        common_issue.submit_solution_time = datetime.now()
        if common_issue.attachment and common_issue.attachment.strip():
            common_issue.attachment = '#'.join([tmp.strip() for tmp in common_issue.attachment.split('#') if tmp]
            + [tmp.strip() for tmp in attachment.split('#') if tmp.strip()])
        else:
            common_issue.attachment = attachment

        db.session.add(common_issue)
        db.session.commit()

        project_obj = common_issue.project

        if common_issue.effect_scope == '平台':
            msg = Message(
                u'[TSDS] %s平台 共性问题——%s(解决方案)' % (common_issue.platform_name, common_issue.name),
                sender='<EMAIL>',
                recipients=[common_issue.developer.email, '<EMAIL>']
            )
        elif common_issue.effect_scope == '产品':
            msg = Message(
                u'[TSDS] %s产品 共性问题——%s(解决方案)' % (project_obj.name, common_issue.name),
                sender='<EMAIL>',
                recipients=[common_issue.developer.email, '<EMAIL>']
            )
        else:
            msg = Message(
                u'[TSDS] %s%s 共性问题——%s(解决方案)' % (project_obj.name, project_obj.customer_area, common_issue.name),
                sender='<EMAIL>',
                recipients=[common_issue.developer.email, common_issue.spm.email, common_issue.tester.email,
                            '<EMAIL>']
            )
        msg.html = '<h5>项目共性问题名称:</h5>{0}<br><br>' \
                   '<h5>问题创建人:</h5>{1}<{2}><br><br>' \
                   '<h5>提出问题项目:</h5>{3}<br><br>' \
                   '<h5>项目所属平台名称:</h5>{4}<br><br>' \
                   '<h5>项目当前基线:</h5>{5}<br><br>' \
                   '<h5>问题描述:</h5>{6}<br>'\
                   '<h5>解决方案:</h5>{7}<br>'.format(common_issue.name, current_user.display_name, current_user.email,
                                                  common_issue.project.name, common_issue.platform_name,
                                                  common_issue.baseline_name, common_issue.description,
                                                  common_issue.solutions)
        mail.send(msg)

        return redirect(url_for('project.common_issue_page'))

    issue_category_options = ['平台', '产品', '订单']
    issue_from_options = []
    issue_from_param = SystemParameter.query.filter_by(key='ISSUE_FROM').first()
    if issue_from_param:
        issue_from_options = [tmp for tmp in str(issue_from_param.value).split('\n') if tmp.strip()]

    return render_template('project/android/common_issue/common_issue_edit.html',
                           common_issue=common_issue,
                           all_project=all_project,
                           all_developer=all_developer,
                           all_spm=all_spm,
                           all_platform=all_platform,
                           attachment=attachment,
                           issue_category_options=issue_category_options,
                           issue_from_options=issue_from_options)


@project.route('/spm_edit_common_issue/<int:common_issue_id>', methods=['GET', 'POST'])
@login_required
def spm_edit_common_issue(common_issue_id):
    """spm确认项目共性问题"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '修改问题' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index + 1]
            break
    else:
        session['breadcrumbs'].append(
            {'text': '修改问题', 'url': url_for('project.dev_edit_common_issue', common_issue_id=common_issue_id)})

    common_issue = CommonIssue.query.filter_by(id=common_issue_id).first()
    if common_issue.attachment:
        attachment = [(tmp, os.path.basename(tmp))for tmp in common_issue.attachment.split('#')]
    else:
        attachment = []

    all_project = [tmp for tmp in ProjectAndroid.query.order_by(ProjectAndroid.create_time.desc()).all() if tmp.status != '关闭']
    all_platform = BPlatform.query.all()
    all_developer = User.query.join(Role).filter(db.and_(User.activate==True, Role.name=='Android开发工程师')).all()
    all_spm = User.query.join(Role).filter(db.and_(User.activate==True, Role.name=='spm')).all()

    if request.method == 'POST':
        name = request.form.get('name', '').strip()
        description = request.form.get('description', '').strip()
        solutions = request.form.get('solutions', '').strip()
        effect_scope = ','.join(request.form.getlist('effect_scope'))
        platform_name = request.form.get('platform_name', '')
        baseline_name = request.form.get('baseline_name', '').strip()
        attachment = request.form.get('attachment', '')
        category = request.form.get('category', '')
        issue_from = request.form.get('issue_from', '')

        other = CommonIssue.query.filter(CommonIssue.name == name).first()
        if other and other.id != common_issue_id:
            flash(u'同名的共性问题已存在', 'warning')
        else:
            common_issue.name = name
            common_issue.description = description
            common_issue.category = category
            common_issue.solutions = solutions
            if effect_scope != ',':
                common_issue.effect_scope = effect_scope
            common_issue.platform_name = platform_name
            common_issue.baseline_name = baseline_name

            email_to_list = request.form.get('to_list', '')
            common_issue.to_list = ';'.join([tmp.strip() for tmp in email_to_list.split(';') if tmp.strip()])
            common_issue.status = 4
            common_issue.issue_from = issue_from
            common_issue.confirm_time = datetime.now()
            if common_issue.attachment and common_issue.attachment.strip():
                common_issue.attachment = '#'.join([tmp.strip() for tmp in common_issue.attachment.split('#') if tmp]
                + [tmp.strip() for tmp in attachment.split('#') if tmp.strip()])
            else:
                common_issue.attachment = attachment

            # 在各个受影响的项目中创建JIRA任务
            task_list = []
            if common_issue.effect_scope != '':
                jira_client = JiraClient()
                # 在JIRA中直接给受影响的项目创建JIRA任务并分配给spm
                effect_project_jira_key = [tmp.project_key for tmp in ProjectAndroid.query.filter(
                    ProjectAndroid.id.in_(common_issue.effect_scope.split(','))).all()]
                for project_jira_key in effect_project_jira_key:
                    task_list.append(jira_client.create_issue(project_jira_key, common_issue.jira_key,
                                                              common_issue.solutions))

            to_list = []
            for tmp in common_issue.to_list.split(';'):
                if tmp.strip() and tmp.endswith('@tinno.com'):
                    to_list.append(tmp)

            # 添加创建者的邮箱
            to_list.append(common_issue.tester.email)
            to_list.append(common_issue.developer.email)
            to_list.append(common_issue.spm.email)
            to_list.append('<EMAIL>')

            # 新增任务的Key
            new_task_key = []
            for task in task_list:
                if task[1] and task[1] not in to_list and task[1].endswith('@tinno.com'):
                    to_list.append(task[1])
                new_task_key.append('项目名称:{0}  负责人:{1}  新增任务KEY:{2}'.format(task[0], task[2], task[3]))

            common_issue.effect_project_new_task_key = '\n'.join(new_task_key)
            db.session.add(common_issue)
            db.session.commit()
            if current_user.email not in to_list:
                to_list.append(current_user.email)

            system_param = SystemParameter.query.filter_by(key='android_project_common_issue_email').first()
            for email in system_param.value.split('\n'):
                if email and email not in to_list:
                    to_list.append(email)

            to_list = list(set(to_list))

            # 邮件通知
            if common_issue.effect_scope == '平台':
                msg = Message(
                    u'[TSDS] %s平台 共性问题——%s' % (common_issue.platform_name, common_issue.name),
                    sender='<EMAIL>',
                    recipients=to_list
                )
            elif common_issue.effect_scope == '产品':
                msg = Message(
                    u'[TSDS] %s产品 共性问题——%s' % (common_issue.project.name, common_issue.name),
                    sender='<EMAIL>',
                    recipients=to_list
                )
            else:
                msg = Message(
                    u'[TSDS] %s%s 共性问题——%s' % (common_issue.project.name, common_issue.project.customer_area, common_issue.name),
                    sender='<EMAIL>',
                    recipients=to_list
                )
            msg.html = '<h5>项目共性问题名称:</h5>{0}<br><br>' \
                       '<h5>问题创建人:</h5>{1}<{2}><br><br>' \
                       '<h5>提出问题项目:</h5>{3}<br><br>' \
                       '<h5>项目所属平台名称:</h5>{4}<br><br>' \
                       '<h5>项目当前基线:</h5>{5}<br><br>' \
                       '<h5>问题描述:</h5>{6}<br><br>' \
                       '<h5>解决方案:</h5>{7}<br><br>' \
                       '<h5>已创建的任务:</h5>{8}<br><br>'.format(
                common_issue.name, current_user.display_name,current_user.email,common_issue.project.name,
                common_issue.platform_name, common_issue.baseline_name, common_issue.description.replace('\n', '<br>'),
                 '<br>'.join(['%s <a href="http://jira.tinno.com:8080/browse/%s">%s</a> %s' %
                              (task[3], task[0], task[0], task[2])for task in task_list]), common_issue.solutions)
            mail.send(msg)

            return redirect(url_for('project.common_issue_page'))

    issue_category_options = ['平台', '产品', '订单']
    issue_from_options = []
    issue_from_param = SystemParameter.query.filter_by(key='ISSUE_FROM').first()
    if issue_from_param:
        issue_from_options = [tmp for tmp in str(issue_from_param.value).split('\n') if tmp.strip()]
    return render_template('project/android/common_issue/common_issue_edit.html',
                           common_issue=common_issue,
                           all_project=all_project,
                           all_developer=all_developer,
                           all_spm=all_spm,
                           all_platform=all_platform,
                           attachment=attachment,
                           issue_category_options=issue_category_options,
                           issue_from_options=issue_from_options)


@project.route('/get_other_project')
@login_required
def get_other_project():
    """
    获取会影响到的项目，获取范围规则是
    平台问题，就获取同平台的所有项目；
    产品问题，就获取此产品的所有订单项目；
    订单问题，就获取同客户的所有项目(同一个Android版本)
    :return:
    """

    project_id = request.args.get('project_id', '')
    platform_name = request.args.get('platform_name', '')
    issue_category = request.args.get('issue_category', '')

    results = []
    # 提出共性问题的项目
    current_project = ProjectAndroid.query.filter_by(id=project_id).first()

    if issue_category == '平台' and platform_name:
        for project in ProjectAndroid.query.filter(ProjectAndroid.platform_name == platform_name).all():
            current_app.logger.info(project.id)
            if project == current_project:
                continue
            if project.parent:
                results.append({"id": project.id, "name": '%s_%s_%s' % (project.parent.name, project.name,
                                                                        project.android_version)})
            else:
                results.append({"id": project.id, "name": '%s_%s' % (project.name, project.android_version)})
    elif issue_category == '产品':
        # 当前项目是产品项目时获取所有基于此项目的子项目
        if current_project.customer_name and current_project.parent:
            for project in ProjectAndroid.query.filter(ProjectAndroid.parent_id == current_project.parent_id).all():
                if project == current_project:
                    continue
                if project.parent:
                    results.append({"id": project.id, "name": '%s_%s_%s' % (project.parent.name, project.name,
                                                                            project.android_version)})
                else:
                    results.append({"id": project.id, "name": '%s_%s' % (project.name, project.android_version)})
        else:
            for project in ProjectAndroid.query.filter(ProjectAndroid.parent_id == project_id).all():
                if project == current_project:
                    continue
                if project.parent:
                    results.append({"id": project.id, "name": '%s_%s_%s' % (project.parent.name, project.name,
                                                                            project.android_version)})
                else:
                    results.append({"id": project.id, "name": '%s_%s' % (project.name, project.android_version)})
    elif issue_category == '订单':
        if current_project.customer_name:
            for project in ProjectAndroid.query.filter(
                    db.and_(ProjectAndroid.customer_name == current_project.customer_name,
                            ProjectAndroid.android_version == current_project.android_version)).all():
                if project == current_project:
                    continue
                if project.parent:
                    results.append({"id": project.id, "name": '%s_%s_%s' % (project.parent.name, project.name,
                                                                            project.android_version)})
                else:
                    results.append({"id": project.id, "name": '%s_%s' % (project.name, project.android_version)})

    return json.dumps(results)


@project.route('/common_issue_delete_file')
@login_required
def common_issue_delete_file():
    """删除项目共性问题附件"""

    common_issue_id = request.args.get('common_issue_id', '')
    file_path = request.args.get('file_path', '')

    common_issue = CommonIssue.query.filter(CommonIssue.id == common_issue_id).first()
    new_attachment = []
    for tmp in common_issue.attachment.split('#'):
        if tmp and tmp != file_path:
            new_attachment.append(tmp)

    common_issue.attachment = '#'.join(new_attachment)
    db.session.add(common_issue)
    db.session.commit()

    return '{"result": true}'


@project.route('/common_issue_get_jira_key_info')
@login_required
def common_issue_get_jira_key_info():
    """获取原始JIRA_KEY的标题和描述"""

    task_key = request.args.get('jira_key', '')
    if task_key:

        project = ProjectAndroid.query.filter_by(project_key=task_key.split('-')[0]).first()
        if project:
            project_id = project.id

        try:
            jira_client = JiraClient()
            title, description = jira_client.get_issue_info(task_key)
        except JIRAError, e:
            current_app.logger.info(e)
            title, description = '', ''
    else:
         project_id, title, description = '', '', ''

    result = [{"title": title, "description": description, "project_id": project_id}]
    return json.dumps(result)


@project.route('/export_common_issue')
@login_required
def export_common_issue():
    """导出共性问题"""

    search = current_app.logger.info(session['search'])
    if search:
        all_record = CommonIssue.query.filter(
            db.or_(CommonIssue.name.like('%' + search + '%'),
                   CommonIssue.description.like('%' + search + '%'),
                   CommonIssue.baseline_name.like('%' + search + '%'),
                   CommonIssue.platform_name.like('%' + search + '%'))).order_by(CommonIssue.submit_issue_time.desc()).all()
    else:
        all_record = CommonIssue.query.order_by(CommonIssue.submit_issue_time.desc()).all()

    excel_file_path = os.path.join(current_app.config['TMS_TEMP_DIR'], "common_issue/%s.xls" % ('项目共性问题_' + str(date.today())))
    excel_option = ExcelOptions( excel_file_path)
    excel_option.export_common_issue_xls(all_record)

    return '{"result": true, "filePath":"%s"}' % excel_file_path
<file_sep># -*- encoding:utf-8 -*-
"""
项目及版本信息模块
"""

from tms import db

class project_task_type(db.Model):
    """项目"""

    __tablename__ = 'project_task_type'

    id = db.Column(db.Integer, primary_key=True)

    task_type = db.Column(db.String(20))

    state = db.Column(db.Integer)


    def __repr__(self):
        return u"<project_task_type> %s" % self.task_type


class project_task_list(db.Model):

    __tablename__ = 'project_task_list'

    id = db.Column(db.Integer, primary_key=True)
    task_type=db.Column(db.Integer, db.ForeignKey('project_task_type.id'))
    type_1 = db.relationship('project_task_type', foreign_keys=[task_type], backref=db.backref('project_type1', order_by=id))
    taskname=db.Column(db.String(50))
    starttime=db.Column(db.DateTime())
    endtime=db.Column(db.DateTime())
    updatetime = db.Column(db.DateTime())
    tdescribe=db.Column(db.String(500))
    question_log=db.Column(db.String(1000))
    state=db.Column(db.Integer)
    tms_user=db.Column(db.Integer,db.ForeignKey('tms_user.id'))
    user1 = db.relationship('User', foreign_keys=[tms_user], backref=db.backref('peoject_user1', order_by=id))

    schedule=db.Column(db.Integer)

    def __repr__(self):
         return u"<project_mytask_page> %s" % self.taskname


<file_sep>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Created by <EMAIL> on 2016/11/24
"""
定时任务明天上午8点,将8点之前超过三天未关闭的Bug,逐条邮件通知到经办人.
三天是是从Bug创建开始到发邮件当前的时间
"""
import datetime
import sys
import time
from optparse import OptionParser

import smtplib
#多个MIME对象的集合
from email.mime.multipart import MIMEMultipart
#MIME文本对象
from email.mime.text import MIMEText
from email.header import Header

import MySQLdb

reload(sys)
sys.setdefaultencoding('utf-8')


def send_mail(to_list=[], cc_list=[], sub='', content='', appendix_file=''):
    """"send_mail("<EMAIL>","sub","content")
    :param to_list:发给谁
    :param cc_list: 抄送给谁
    :param sub:主题
    :param content:内容
    :param appendix_file: 附件
    """

    # 设置服务器、用户名、口令及邮箱的后缀
    mail_host = '192.168.10.8'
    mail_user = '<EMAIL>'
    mail_pass = '<PASSWORD>'
    sender = "<EMAIL>"
    msg_root = MIMEMultipart()
    # 解决中文乱码
    att_text = MIMEText(content, 'html', 'UTF-8')
    # 添加邮件正文
    msg_root.attach(att_text)

    # 发件人
    msg_root['from'] = sender
    # 邮件标题
    msg_root['Subject'] = Header(sub, 'utf-8')
    # 设置时间
    msg_root['Date'] = time.ctime(time.time())
    msg_root['To'] = ';'.join(to_list)
    if cc_list:
        msg_root['Cc'] = ';'.join(cc_list)

    # 构造附件(附件路径出现中文，会提示编码错误)
    if appendix_file != '':
        rar_file_path = appendix_file
        att = MIMEText(open(rar_file_path, 'rb').read(), 'gbk', 'UTF-8')
        att['Content-Type'] = 'application/octet-stream;name=%s' % Header(rar_file_path, 'UTF-8')
        att['Content-Disposition'] = 'attachment;filename=%s' % Header(rar_file_path, 'UTF-8')
        msg_root.attach(att)

    # 群发邮件
    smtp = smtplib.SMTP(mail_host, 25)
    smtp.login(mail_user, mail_pass)
    if cc_list:
        smtp.sendmail(sender, [to_list, cc_list], msg_root.as_string())
    else:
        smtp.sendmail(sender, to_list, msg_root.as_string())
    # 休眠5秒
    time.sleep(5)
    # 断开与服务器的连接
    smtp.quit()


def get_time_out_bug(pkey, allow_date):
    """
    获取指定项目中和当前时间相比超过三天的任务
    :param pkey:
    :param allow_date:
    :return:
    """

    current_time = datetime.datetime.now()
    start_time = (current_time + datetime.timedelta(days=-allow_date)).strftime('%Y-%m-%d')

    try:
        conn = MySQLdb.connect(host='172.16.20.162', db='gms', user='tup', passwd='tup', charset='utf8')
        cursor = conn.cursor()
        query_sql = "SELECT issuenum, ASSIGNEE, SUMMARY, jiraissue.DESCRIPTION, CREATED, pname FROM jiraissue " \
                    "JOIN project ON jiraissue.PROJECT=project.ID WHERE project.pkey='{0}' " \
                    "AND jiraissue.issuetype !=2 AND jiraissue.issuestatus NOT IN(5,6) " \
                    "AND jiraissue.CREATED > '{1}'".format(pkey, start_time)
        print query_sql
        cursor.execute(query_sql)
        bug_list = [tmp for tmp in cursor.fetchall()]
        cursor.close()
        conn.close()
        return bug_list
    except MySQLdb.Error, e:
        send_mail(['<EMAIL>'], [], '查询超时Bug失败', str(e))
        sys.exit(1)


def main():
    des = u"JIRA Bug Timeout Email Notifier"
    prog = u"JiraBugNotifier"
    ver = "%prog v0.0.1"
    usage = "%prog pkey  timeout"

    parse = OptionParser(description=des, prog=prog, version=ver, usage=usage, add_help_option=True)

    parse.add_option('--pkey', action='store', type='string', dest='pkey', help="project jira key")
    parse.add_option('--date', action='store', type='int', dest='allow_date', default=3, help="allow time out date")

    options, args = parse.parse_args()
    if not options.pkey:
        print '项目JIRA Key不能为空'
        sys.exit(1)

    for bug in get_time_out_bug(options.pkey, options.allow_date):
        print bug



if __name__ == '__main__':
    main()<file_sep>
function table_init() {
    var task_wording_table = $('#task_wording').DataTable({
        "language": {
            "aria": {
                "sortAscending": ": activate to sort column ascending",
                "sortDescending": ": activate to sort column descending"
            },
            "emptyTable": "No data available in table",
            "info": "Showing _START_ to _END_ of _TOTAL_ records",
            "infoEmpty": "No records found",
            "infoFiltered": "(filtered1 from _MAX_ total records)",
            "lengthMenu": "Show _MENU_ records",
            "search": "Search:",
            "zeroRecords": "No matching records found",
            "paginate": {
                "previous": "Prev",
                "next": "Next",
                "last": "Last",
                "first": "First"
            }
        },
        "bServerSide": true, // save datatable state(pagination, sort, etc) in cookie.
        "bProcessing": true,
        "ajax": {
            url: '/task/sub_task_wording_data',
            data: {
                'sub_task_id': function () {
                    return window.sub_task_id;
                }
            },
        },
        "lengthMenu": [
            [10, 50, 200],
            [10, 50, 200] // change per page values here
        ],
        // set the initial value
        "pageLength": 10,
        "pagingType": "bootstrap_full_number"
    });
    return task_wording_table;
}

var sub_task_id;
var task_wording_table;


$('#current_task tbody').on( 'click', 'tr', function () {
    window.sub_task_id = $(this).attr('data');
    if (task_wording_table) {
        task_wording_table.ajax.reload();
    } else {
        task_wording_table = table_init();
    }
    $('#wording_title').text($(this).parent().find("td").eq(1).text()+"'s Wordings");
});

<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

"""
从Gerrit中导所有用户进系统
"""
from MySQLdb import connect
from werkzeug.security import generate_password_hash

# 查询出现有数据
con = connect(host='192.168.33.7', db='tms', user='gms', passwd='gms', charset='utf8')
cursor = con.cursor()

user_sql = "SELECT email FROM tms_user"
cursor.execute(user_sql)
exists_user = [tmp[0].upper() for tmp in cursor.fetchall()]
cursor.close()
con.close()
print exists_user


# 导出数据
con = connect(host='192.168.10.207', db='reviewdb207', user='root', passwd='<PASSWORD>', charset='utf8')
cursor = con.cursor()

user_sql = "SELECT full_name, preferred_email FROM accounts WHERE full_name is not Null " \
           "AND preferred_email is not NULL"
cursor.execute(user_sql)

records = []
for tmp in cursor.fetchall():
    if tmp[1].upper() in exists_user:
        print tmp[1]
    else:
        records.append((tmp[1].split('@')[0], generate_password_hash(tmp[1].split('@')[0]), tmp[0], tmp[1]))
cursor.close()
con.close()

# 导入数据
con = connect(host='192.168.33.7', db='tms', user='gms', passwd='gms', charset='utf8')
cursor = con.cursor()


insert_user = "INSERT into tms_user(username, password_hash, display_name, email) VALUES (%s, %s, %s, %s)"
for record in records:
    print record
    cursor.executemany(insert_user, [record])
    con.commit()

con.close()

<file_sep># -*- coding: utf-8 -*-
# Created by changjie.fan on 2016/9/8
"""
jira操作客户端
"""
from jira import JIRA


class JiraClient:

    def __init__(self):
        options = {
            'server': 'http://jira.tinno.com:8080'
        }
        # jira client
        self.jira_client = JIRA(options=options, basic_auth=('<PASSWORD>', '<PASSWORD>'))
        # self.jira_client = JIRA(options=options, basic_auth=('changjie.fan', '<EMAIL>'))

    def create_issue(self, project_key, task_key, comment):
        """
        判断项目是否有共性问题模块，没有的创建
        :param project_key: 项目jira_key
        :param task_key: 原问题key
        :param comment：备注中填写解决方案
        :return:
        """

        # 源问题及问题所属模块
        src_issue = self.jira_client.issue(task_key)
        src_issue_fields = src_issue.fields
        src_issue_component_names = [component.name for component in src_issue_fields.components]
        # 目的项目
        dst_project = self.jira_client.project(project_key)
        # 目的项目所有的模块
        dst_issue_component_names = {}
        for component in dst_project.components:
            dst_issue_component_names.update({component.name: component})
        dst_issue_component_ids = []

        # 判断目的项目是否有共性问题模块，没有就创建
        if u'共性问题' in dst_issue_component_names.keys():
            dst_issue_component_ids.append(dst_issue_component_names[u'共性问题'])
        else:
            component = self.jira_client.create_component('共性问题', dst_project)
            dst_issue_component_names.update({component.name: component})
            dst_issue_component_ids.append(component)

        # 挑选出源问题所在的模块，用于新问题的创建(新问题按源问题模块创建，并在共性问题模块下创建)
        for src_name in src_issue_component_names:
            if src_name in dst_issue_component_names.keys():
                dst_issue_component_ids.append(dst_issue_component_names[src_name])
            else:
                component = self.jira_client.create_component(src_name, dst_project)
                dst_issue_component_names.update({component.name: component})
                dst_issue_component_ids.append(component)

        print dst_issue_component_ids
        dst_issue_dict = {
            'project': {'key': project_key},
            'summary': u'%s' % src_issue_fields.summary,
            'description': u'%s' % src_issue_fields.description,
            'issuetype': {'id': src_issue_fields.issuetype.id},
            'components': [{'id': tmp.id } for tmp in dst_issue_component_ids],
            'priority': {'id': src_issue_fields.priority.id}
        }
        print dst_issue_dict
        # 创建问题
        dst_issue = self.jira_client.create_issue(fields=dst_issue_dict)
        # 添加问题备注填写解决方案
        self.jira_client.add_comment(dst_issue.key, comment)
        # 创建问题连接
        self.jira_client.create_issue_link('Copy', src_issue, dst_issue)

        # 将影响项目的jira负责人作为任务经办人分配
        project = self.jira_client.project(project_key)
        self.jira_client.assign_issue(dst_issue, project.lead.name)
        print dir(project.lead)
        print dir(dst_issue.fields)
        return dst_issue.key, <EMAIL>" % project.lead.name, project.lead.displayName, project.name


    def get_issue_info(self, jira_key):
        issue = self.jira_client.issue(jira_key)
        return issue.fields.summary, issue.fields.description

    def delete_task(self, jira_key):
        issue = self.jira_client.issue(jira_key)
        issue.delete()


if __name__ == '__main__':
    # jira_clent = JiraClient()
    # print jira_clent.create_issue('SCM', 'PUBLIC-33', 'repo 2path:package/ap.git')
    # JiraClient.create_issue('SCM', "PUBISSUE-1456")
    # jira_client = JiraClient()
    # print jira_client.get_issue_info('SCM-4')
    pass
<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""
from tms import db


class RepositoryServer(db.Model):
    """
    Git 服务器
    """
    __tablename__ = 'repo_server'

    id = db.Column(db.Integer, primary_key=True)
    # 服务器名称
    name = db.Column(db.String(64))
    # 服务器ip地址
    ip_address = db.Column(db.String(15))
    # 服务器所有仓库的根目录, 通常是/home/git/repositories 主要在gerrit上可能会有不同
    root_path = db.Column(db.String(200), default='/home/git/repositories')
    # 在备份服务器上的地址
    backup_server_path = db.Column(db.String(200))
    # 克隆是的:前面的部分，主要也是为了兼容gerrit 通常是git@ip  gerrit是ssh://ip:29418
    repo_url = db.Column(db.String(200))

    # 服务器状态 True表示正常， False表示停用
    status = db.Column(db.Boolean, default=True)
    # config的中远程名称
    remote_name = db.Column(db.String(64))

    def __repr__(self):
        return "<RepoServer %r>" % self.name


class Platform(db.Model):
    """
    仓库平台
    """
    __tablename__ = 'repo_platform'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(500))
    path = db.Column(db.String(700))
    description = db.Column(db.String(200))

    server_id = db.Column(db.Integer, db.ForeignKey('repo_server.id'))
    server = db.relationship('RepositoryServer', backref=db.backref('repo_platform', order_by=id))

    # 平台状态 True表示正常， False表示停用
    status = db.Column(db.Boolean, default=True)

    def __repr__(self):
        return "<RepoPlatform %r>" % self.name


class Repository(db.Model):
    """
    Git仓库
    """
    __tablename__ = 'repo_repo'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(500))
    path = db.Column(db.String(700))
    # 1 project_code 2 app code 3 app release 4 manifest
    category = db.Column(db.Integer, default=1)
    description = db.Column(db.String(200))
    repo_url = db.Column(db.String(300))

    platform_id = db.Column(db.Integer, db.ForeignKey('repo_platform.id'))
    platform = db.relationship('Platform', backref=db.backref('repo_repo', order_by=id))

    # 仓库状态 True表示正常， False表示停用
    status = db.Column(db.Boolean, default=True)

    def __repr__(self):
        return "<Repo %r>" % self.name


class Reference(db.Model):
    """
    Git 仓库的应用表/分支或tag
    """

    __tablename__ = 'repo_refs'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(200))
    # 0 表示tag， 1表示分支
    category = db.Column(db.Integer, default=0)

    # 0 表示正常， 1 表示已删除
    status = db.Column(db.Integer, default=0)

    # 删除此引用的记录id
    push_id = db.Column(db.Integer, nullable=True)

    repository_id = db.Column(db.Integer, db.ForeignKey('repo_repo.id'))
    repository = db.relationship('Repository', backref=db.backref('refs', order_by=id))

    def __repr__(self):
        return "<Reference %r>" % self.name


class Commit(db.Model):
    """
    Git Commit 记录信息
    """

    __tablename__ = 'repo_commit'

    id = db.Column(db.Integer, primary_key=True)
    # commit id的hash值
    commit_hash = db.Column(db.String(40))
    # 父commit的hash值，多个值用空格隔开
    parent_hash = db.Column(db.String(200), nullable=True)
    # tree对象
    tree_hash = db.Column(db.String(40), nullable=True)
    author_name = db.Column(db.String(64), nullable=True)
    author_email = db.Column(db.String(64), nullable=True)
    author_time = db.Column(db.DateTime, nullable=True)
    committer_name = db.Column(db.String(64))
    committer_email = db.Column(db.String(64))
    commit_time = db.Column(db.DateTime)
    message = db.Column(db.String(500), nullable=True)

    repository_id = db.Column(db.Integer, db.ForeignKey('repo_repo.id'))
    repository = db.relationship('Repository', backref=db.backref('repo_refs', order_by=id))

    def __repr__(self):
        return "<Commit %r>" % self.commit_hash


# 用户和组的多对多
repo_user_group = db.Table('repo_user_group', db.metadata,
                           db.Column('user_id', db.Integer, db.ForeignKey('repo_user.id')),
                           db.Column('group_id', db.Integer, db.ForeignKey('repo_group.id')))

# 用户和服务器的多对多
repo_user_server = db.Table('repo_user_server', db.metadata,
                            db.Column('user_id', db.Integer, db.ForeignKey('repo_user.id')),
                            db.Column('server_id', db.Integer, db.ForeignKey('repo_server.id')))


class GitUser(db.Model):
    """
    Git 用户
    """
    __tablename__ = 'repo_user'

    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64))
    email_address = db.Column(db.String(64), unique=True)
    ssh_key = db.Column(db.String(300))

    # 用户状态True正常, False停用
    activate = db.Column(db.Boolean, default=True)
    group = db.relationship('GitUserGroup', secondary='repo_user_group', backref='repo_user')
    server = db.relationship('RepositoryServer', secondary='repo_user_server', backref='repo_user')

    def __repr__(self):
        return "<GitUser %r>" % self.username

# 用户组和服务器的多对多
repo_group_server = db.Table('repo_group_server', db.metadata,
                             db.Column('group_id', db.Integer, db.ForeignKey('repo_group.id')),
                             db.Column('server_id', db.Integer, db.ForeignKey('repo_server.id')))


class GitUserGroup(db.Model):
    """
    Git 用户组
    1. 管理员组有最大的权限
    2. read读权限
    3。 write 写权限
    """
    __tablename__ = 'repo_group'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    description = db.Column(db.String(300))

    # 用户组状态True正常, False停用
    status = db.Column(db.Boolean, default=True)
    # 用户组可以在多台服务器上存在
    server = db.relationship('RepositoryServer', secondary='repo_group_server', backref='repo_group')

    def __repr__(self):
        return "<GitUserGroup %r>" % self.name


class Permission(db.Model):
    """
    Git仓库或平台权限
    """
    __tablname__ = 'repo_permission'

    id = db.Column(db.Integer, primary_key=True)
    rights = db.Column(db.Text)

    repository_id = db.Column(db.Integer, db.ForeignKey('repo_repo.id'))
    repository = db.relationship('Repository', backref=db.backref('repo_permission', order_by=id))

    platform_id = db.Column(db.Integer, db.ForeignKey('repo_platform.id'))
    platform = db.relationship('Platform', backref=db.backref('repo_permission', order_by=id))

    server_id = db.Column(db.Integer, db.ForeignKey('repo_server.id'))
    server = db.relationship('RepositoryServer', backref=db.backref('repo_permission', order_by=id))

    def __repr__(self):
        return "<Permission %r>" % ('permission:'+self.id)


class PushHistory(db.Model):
    """
    developer推送的历史记录
    """
    __tablename__ = 'repo_push_history'

    id = db.Column(db.Integer, primary_key=True)
    # 当前Git服务器的Ip地址
    server_ip = db.Column(db.String(15))
    # Git仓库的完整路径
    repository_path = db.Column(db.String(500))
    # 推送的Tag
    reference_name = db.Column(db.String(255))
    # 更新的commits
    commits = db.Column(db.Text)
    # 推送时间
    push_time = db.Column(db.DateTime())
    # 推送人
    push_user = db.Column(db.String(64))
    # 推送类型 new branch/new tag/delete branch/delete tag/ update
    push_type = db.Column(db.String(64))
    # command
    command = db.Column(db.String(255))
    # 本次push所有commit的diff信息
    diff_info = db.Column(db.Text)
    # 处理状态0 表示未同步备份服务器，1表示已同步到备份服务器，2表示处理出错了
    status = db.Column(db.Integer, default=0)
    # 处理出错的信息
    error_info = db.Column(db.String(200))
    # 备注信息
    note = db.Column(db.String(200))
    # 备份时间
    backup_time = db.Column(db.DateTime())
    # commit和reference信息加入数据库时间
    import_time = db.Column(db.DateTime())
    # 实时备份状态 0表示未实时备份， 1 表示已实时备份
    sync_status = db.Column(db.Integer, default=0)
    # 实时备份的时间
    sync_time = db.Column(db.DateTime)

    def __repr__(self):
        return '<PushHistory %r>' % self.reference_name


class PullHistory(db.Model):
    """
    developer pull和clone历史记录
    """
    __tablename__ = 'repo_pull_history'

    id = db.Column(db.Integer, primary_key=True)
    # 当前Git服务器的Ip地址
    server_ip = db.Column(db.String(15))
    # Git仓库的完整路径
    repository_path = db.Column(db.String(500))
    # 下载时间
    pull_time = db.Column(db.DateTime())
    # 下载人
    pull_user = db.Column(db.String(64))
    # 处理出错的信息
    error_info = db.Column(db.String(200))
    # 备注信息
    note = db.Column(db.String(200))

    def __repr__(self):
        return '<PullHistory %r>' % self.reference_name


class GitServeBackupLog(db.Model):
    """Git服务器备份记录"""

    __tablename__ = 'repo_serve_backup_log'

    id = db.Column(db.Integer, primary_key=True)
    # 备份Git服务器的ip地址
    server_ip = db.Column(db.String(15))
    # 平台路径
    platform_path = db.Column(db.String(200))
    # 备份完成时间
    backup_time = db.Column(db.DateTime)
    # 备份结果
    result = db.Column(db.Boolean)
    # 备份日志
    info = db.Column(db.Text)

    def __repr__(self):
        return '<GitServeBackupLog %r>' % self.backup_time<file_sep>/**
 * Created by changjie.fan on 2016/5/6.
 */
var is_all = false;
var test_case_table = $('#test_case_table').DataTable({
    "language": {
        "aria": {
            "sortAscending": ": activate to sort column ascending",
            "sortDescending": ": activate to sort column descending"
        },
        "emptyTable": "No data available in table",
        "info": "Showing _START_ to _END_ of _TOTAL_ records",
        "infoEmpty": "No records found",
        "infoFiltered": "(filtered1 from _MAX_ total records)",
        "lengthMenu": "Show _MENU_ records",
        "search": "Search:",
        "zeroRecords": "No matching records found",
        "paginate": {
            "previous": "Prev",
            "next": "Next",
            "last": "Last",
            "first": "First"
        }
    },
    "bFilter": false,
    "bServerSide": true, // save datatable state(pagination, sort, etc) in cookie.
    "bProcessing": true,
    "ajax": {
        url: '/qa/test_case_data',
        data: {
            is_all: function () {
                return window.is_all;
            }
        },
    },
    "columns": [
        {"data": 'index'},
        {"data": 'number'},
        {"data": 'level'},
        {"data": 'test_type'},
        {"data": 'module_name'},
        {"data": 'standard_sdk'},
        {"data": 'class_name'},
        {"data": 'method_name'}
    ],
    "lengthMenu": [
        [10, 50, 200],
        [10, 50, 200] // change per page values here
    ],
    // set the initial value
    "pageLength": 10,
    "pagingType": "bootstrap_full_number"
});

function all_test_case() {
    is_all = true;
    test_case_table.ajax.reload();
}

$('#test_case_table tbody').on( 'dblclick', 'tr', function () {
    $(this).toggleClass('selected');
    window.location.href=$SCRIPT_ROOT + '/qa/edit_test_case/'+test_case_table.row($(this)).data().id;
});
$("#test_case_table tr").live({
    mouseover:function(){
        $(this).css("cursor","pointer");
    }
});<file_sep>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Created by changjie.fan on 2016/8/25
"""
"""
from tms import db


class BPlatform(db.Model):
    """厂商平台"""

    __tablename__ = 'bs_platform'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64))
    # True正常、False取消
    flag = db.Column(db.Boolean, default=True)
    # 备注
    note = db.Column(db.String(300))

    def __repr__(self):
        return '<Platform %r>' % self.name<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""

from . import build

from flask.ext.login import login_user, logout_user, login_required, current_user
from flask import render_template, redirect, url_for, request, flash, current_app, session, send_file


from tms.project.androidProjectModules import *
from tms.auth.models import User, Role
from tms.build.models import *

@build.route('/module_compile_para_page')
@login_required
def module_compile_para_page():
    """
    模块编译参数
    :return:
    """

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '模块编译参数', 'url': url_for('build.module_compile_para_page')})
    session['menu_path'] = ['编译管理', '模块编译参数']

    query_type = 0

    # 模块编译参数
    modules = Module.query.order_by(Module.platform_id.asc()).all()

    result = []
    index = 1
    for module in modules:

        result.append((index, module))

        index += 1


    return render_template('build/module_compile_para_page.html', module_paras=result,query_type=query_type)



@build.route('/compile_mode_page')
@login_required
def compile_mode_page():
    """
    编译模式
    :return:
    """

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '模块编译参数', 'url': url_for('build.module_compile_para_page')})
    session['menu_path'] = ['编译管理', '模块编译参数']

    query_type = 1

    # 模块编译参数
    modules = CompileMode.query.all()

    result = []
    index = 1
    for module in modules:

        result.append((index, module))

        index += 1


    return render_template('build/module_compile_para_page.html', module_paras=result,query_type=query_type)


@build.route('/compile_order_page')
@login_required
def compile_order_page():
    """
    编译命令
    :return:
    """

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '模块编译参数', 'url': url_for('build.module_compile_para_page')})
    session['menu_path'] = ['编译管理', '模块编译参数']

    query_type = 2

    # 模块编译参数
    modules = CompileOrder.query.all()

    result = []
    index = 1
    for module in modules:

        result.append((index, module))

        index += 1


    return render_template('build/module_compile_para_page.html', module_paras=result,query_type=query_type)


@build.route('/compile_type_page')
@login_required
def compile_type_page():
    """
    编译类型
    :return:
    """

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '模块编译参数', 'url': url_for('build.module_compile_para_page')})
    session['menu_path'] = ['编译管理', '模块编译参数']

    query_type = 3

    # 模块编译参数
    modules = CompileType.query.all()

    result = []
    index = 1
    for module in modules:

        result.append((index, module))

        index += 1


    return render_template('build/module_compile_para_page.html', module_paras=result,query_type=query_type)


@build.route('/platform_page')
@login_required
def platform_page():
    """
    平台名称
    :return:
    """

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '模块编译参数', 'url': url_for('build.module_compile_para_page')})
    session['menu_path'] = ['编译管理', '模块编译参数']

    query_type = 4

    # 模块编译参数
    modules = PlatForm.query.all()

    result = []
    index = 1
    for module in modules:

        result.append((index, module))

        index += 1


    return render_template('build/module_compile_para_page.html', module_paras=result,query_type=query_type)


@build.route('/add_module_compile_para_page',methods=['GET', 'POST'])
@login_required
def add_module_compile_para_page():
    """
    添加模块编译参数
    :return:
    """

    if request.method == 'GET':

        session['breadcrumbs'] = [session['breadcrumbs'][0]]
        session['breadcrumbs'].append({'text': '模块编译参数', 'url': url_for('build.module_compile_para_page')})
        session['menu_path'] = ['编译管理', '模块编译参数']

        record = {}

        operation_type = ['添加模块信息','添加平台信息','添加编译类型信息','添加编译命令信息','添加编译模式信息']

        platforms = PlatForm.query.all()

        return render_template('build/add_module_compile_para_page.html',record=record,operation_type=operation_type,platforms=platforms)

    else:

        operation_type = request.form['operation_type']

        if operation_type == '添加模块信息':

            pro_name = request.form['pro_name'].strip()
            module_name = request.form['module_name'].strip()
            platform_id = request.form['platform'].strip()
            command = request.form['command'].strip().strip('\n')
            path = request.form['path'].strip()

            exists_module = Module.query.filter(Module.platform_id == platform_id,Module.module_name == module_name,Module.pro_name == pro_name).first()

            if exists_module is not None:
                flash(u'相同模块的命令"{0}"已存在'.format(exists_module.module_name), 'danger')
            else:

                module = Module()
                module.module_name = module_name.strip()
                module.platform_id = platform_id
                module.pro_name = pro_name.strip()
                module.command = command.strip().strip('\n')
                module.path = path.strip()

                db.session.add(module)
                db.session.commit()

                flash(u'新增成功!', 'info')
            return redirect(url_for('build.module_compile_para_page'))

        elif operation_type == '添加平台信息':

            name = request.form['name'].strip()


            exists_module = PlatForm.query.filter(PlatForm.name == name).first()

            if exists_module is not None:
                flash(u'相同名称的平台"{0}"已存在'.format(exists_module.name), 'danger')
            else:

                module = PlatForm()
                module.name = name.strip()

                db.session.add(module)
                db.session.commit()

                flash(u'新增成功!', 'info')
            return redirect(url_for('build.module_compile_para_page'))

        elif operation_type == '添加编译类型信息':

            name = request.form['name'].strip()


            exists_module = CompileType.query.filter(CompileType.compile_type == name).first()

            if exists_module is not None:
                flash(u'相同名称的编译类型"{0}"已存在'.format(exists_module.compile_type), 'danger')
            else:

                module = CompileType()
                module.compile_type = name.strip()

                db.session.add(module)
                db.session.commit()

                flash(u'新增成功!', 'info')
            return redirect(url_for('build.module_compile_para_page'))

        elif operation_type == '添加编译命令信息':

            name = request.form['name'].strip()


            exists_module = CompileOrder.query.filter(CompileOrder.compile_order == name).first()

            if exists_module is not None:
                flash(u'相同名称的编译命令"{0}"已存在'.format(exists_module.compile_order), 'danger')
            else:

                module = CompileOrder()
                module.compile_order = name.strip()

                db.session.add(module)
                db.session.commit()

                flash(u'新增成功!', 'info')
            return redirect(url_for('build.module_compile_para_page'))

        elif operation_type == '添加编译模式信息':

            name = request.form['name'].strip()


            exists_module = CompileMode.query.filter(CompileMode.compile_mode == name).first()

            if exists_module is not None:
                flash(u'相同名称的编译模式"{0}"已存在'.format(exists_module.compile_mode), 'danger')
            else:

                module = CompileMode()
                module.compile_mode = name.strip()

                db.session.add(module)
                db.session.commit()

                flash(u'新增成功!', 'info')
            return redirect(url_for('build.module_compile_para_page'))


@build.route('/edit_module_compile_para_page/<string:id>',methods=['GET', 'POST'])
@login_required
def edit_module_compile_para_page(id):
    """
    编辑模块编译参数
    :return:
    """

    if request.method == 'GET':

        session['breadcrumbs'] = [session['breadcrumbs'][0]]
        session['breadcrumbs'].append({'text': '模块编译参数', 'url': url_for('build.module_compile_para_page')})
        session['menu_path'] = ['编译管理', '模块编译参数']

        if str(id).startswith('compile_mode_'):
            #编译模式

            array = str(id).split('_')
            db_id = array[len(array) - 1]

            compileMode = CompileMode.query.filter(CompileMode.id == db_id).first()

            module = {}
            module['id'] = compileMode.id
            module['name'] = compileMode.compile_mode

            query_type = 1

            return render_template('build/edit_module_compile_para_page.html',query_type=query_type,module=module)

        elif str(id).startswith('compile_order_'):
            #编译命令

            array = str(id).split('_')
            db_id = array[len(array) - 1]

            compileOrder = CompileOrder.query.filter(CompileOrder.id == db_id).first()

            module = {}
            module['id'] = compileOrder.id
            module['name'] = compileOrder.compile_order

            query_type = 2

            return render_template('build/edit_module_compile_para_page.html',query_type=query_type,module=module)

        elif str(id).startswith('compile_type_'):
            #编译类型

            array = str(id).split('_')
            db_id = array[len(array) - 1]

            compileType = CompileType.query.filter(CompileType.id == db_id).first()

            module = {}
            module['id'] = compileType.id
            module['name'] = compileType.compile_type

            query_type = 3

            return render_template('build/edit_module_compile_para_page.html',query_type=query_type,module=module)

        elif str(id).startswith('platform_'):
            #平台

            array = str(id).split('_')
            db_id = array[len(array) - 1]

            platForm = PlatForm.query.filter(PlatForm.id == db_id).first()

            module = {}
            module['id'] = platForm.id
            module['name'] = platForm.name

            query_type = 4

            return render_template('build/edit_module_compile_para_page.html',query_type=query_type,module=module)

        elif str(id).startswith('module_name_'):
            #模块--命令路径

            array = str(id).split('_')
            db_id = array[len(array) - 1]

            module = Module.query.filter(Module.id == db_id).first()

            platForms = PlatForm.query.all()

            query_type = 5

            return render_template('build/edit_module_compile_para_page.html',query_type=query_type,module=module,platForms=platForms)

    else:

        query_type = request.form['query_type']

        if query_type == '1':
            #compileMode

            name = request.form['name'].strip()

            exists_module = CompileMode.query.filter(CompileMode.id != id,CompileMode.compile_mode == name).first()

            if exists_module is not None:
                flash(u'相同编译模式"{0}"已存在'.format(exists_module.compile_mode), 'danger')
            else:

                module = CompileMode.query.filter(CompileMode.id == id).first()
                module.compile_mode = name

                db.session.add(module)
                db.session.commit()

                flash(u'修改成功!', 'info')
            return redirect(url_for('build.module_compile_para_page'))

        elif query_type == '2':
            #CompileOrder

            name = request.form['name'].strip()

            exists_module = CompileOrder.query.filter(CompileOrder.id != id,CompileOrder.compile_order == name).first()

            if exists_module is not None:
                flash(u'相同编译命令"{0}"已存在'.format(exists_module.compile_order), 'danger')
            else:

                module = CompileOrder.query.filter(CompileOrder.id == id).first()
                module.compile_order = name

                db.session.add(module)
                db.session.commit()

                flash(u'修改成功!', 'info')
            return redirect(url_for('build.module_compile_para_page'))

        elif query_type == '3':
            #CompileType

            name = request.form['name'].strip()

            exists_module = CompileType.query.filter(CompileType.id != id,CompileType.compile_type == name).first()

            if exists_module is not None:
                flash(u'相同编译类型"{0}"已存在'.format(exists_module.compile_type), 'danger')
            else:

                module = CompileType.query.filter(CompileType.id == id).first()
                module.compile_type = name

                db.session.add(module)
                db.session.commit()

                flash(u'修改成功!', 'info')
            return redirect(url_for('build.module_compile_para_page'))

        elif query_type == '4':
            #PlatForm

            name = request.form['name'].strip()

            exists_module = PlatForm.query.filter(PlatForm.id != id,PlatForm.name == name).first()

            if exists_module is not None:
                flash(u'相同平台名称"{0}"已存在'.format(exists_module.name), 'danger')
            else:

                module = PlatForm.query.filter(PlatForm.id == id).first()
                module.name = name

                db.session.add(module)
                db.session.commit()

                flash(u'修改成功!', 'info')
            return redirect(url_for('build.module_compile_para_page'))

        elif query_type == '5':
            #module_name

            pro_name = request.form['pro_name'].strip()
            module_name = request.form['module_name'].strip()
            platform_id = request.form['platform'].strip()
            command = request.form['command'].strip().strip('\n')
            path = request.form['path'].strip()

            exists_module = Module.query.filter(Module.id != id,Module.module_name == module_name,Module.pro_name == pro_name,Module.platform_id == platform_id).first()

            if exists_module is not None:
                flash(u'相同项目名称,模块名称的模块"{0}"已存在'.format(exists_module.module_name), 'danger')
            else:

                module = Module.query.filter(Module.id == id).first()
                module.module_name = module_name.strip()
                module.platform_id = platform_id
                module.pro_name = pro_name.strip()
                module.command = command.strip().strip('\n')
                module.path = path.strip()

                db.session.add(module)
                db.session.commit()

                flash(u'修改成功!', 'info')
            return redirect(url_for('build.module_compile_para_page'))


@build.route('/edit_module_compile_para_info/<int:id>/<int:query_type>')
@login_required
def edit_module_compile_para_info(id,query_type):
    """
    编辑模块编译参数
    :return:
    """

    if request.method == 'GET':

        session['breadcrumbs'] = [session['breadcrumbs'][0]]
        session['breadcrumbs'].append({'text': '模块编译参数', 'url': url_for('build.module_compile_para_page')})
        session['menu_path'] = ['编译管理', '模块编译参数']

        if query_type == 1:
            #编译模式

            compileMode = CompileMode.query.filter(CompileMode.id == id).first()

            module = {}
            module['id'] = compileMode.id
            module['name'] = compileMode.compile_mode

            query_type = 1

            return render_template('build/edit_module_compile_para_page.html',query_type=query_type,module=module)

        elif query_type == 2:
            #编译命令

            compileOrder = CompileOrder.query.filter(CompileOrder.id == id).first()

            module = {}
            module['id'] = compileOrder.id
            module['name'] = compileOrder.compile_order

            query_type = 2

            return render_template('build/edit_module_compile_para_page.html',query_type=query_type,module=module)

        elif query_type == 3:
            #编译类型

            compileType = CompileType.query.filter(CompileType.id == id).first()

            module = {}
            module['id'] = compileType.id
            module['name'] = compileType.compile_type

            query_type = 3

            return render_template('build/edit_module_compile_para_page.html',query_type=query_type,module=module)

        elif query_type == 4:
            #平台

            platForm = PlatForm.query.filter(PlatForm.id == id).first()

            module = {}
            module['id'] = platForm.id
            module['name'] = platForm.name

            query_type = 4

            return render_template('build/edit_module_compile_para_page.html',query_type=query_type,module=module)

        elif query_type == 5:
            #模块--命令路径

            module = Module.query.filter(Module.id == id).first()

            platForms = PlatForm.query.all()

            query_type = 5

            return render_template('build/edit_module_compile_para_page.html',query_type=query_type,module=module,platForms=platForms)

<file_sep>#!/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

"""
Gerrit 钩子
1. 检查提交说明
2. 记录push信息，进行实时同步

"""
import os
import sys
from optparse import OptionParser
import commands
import re
import time

import simplejson as json
import MySQLdb
from MySQLdb import connect

from post_updated import get_ip_address, push_pubisher, send_mail

reload(sys)
sys.setdefaultencoding('utf-8')

# myos配置数据库信息
db_host = '192.168.33.7'
db_user = 'tup'
db_passwd = 'tup'
db_name = 'tms'

# tsds配置数据库信息
jira_db_host = '172.16.5.95'
jira_db_name = 'jiradb6'
jira_db_user = 'jirauser'
jira_db_passwd = '<PASSWORD>'


def check_message_format(rp, msg):
    """检查提交说明格式和JIRA_KEY的有效性
    :param rp: 检查提交说明格式的正则表达式
    :param msg: 提交说明
    """

    m = rp.match(msg)
    format_error = False
    key_error = False
    if m:
        task_key = m.groups()[1]
        if task_key:
            conn = connect(host=jira_db_host, db=jira_db_name, user=jira_db_user, passwd=jira_db_passwd, charset='utf8')
            cursor = conn.cursor()
            check_sql = "select issue.issuenum, issue.SUMMARY from jiraissue issue join project pro where " \
                    "issue.issuenum='%s' and issue.project=pro.id and  pro.pkey='%s'" % (task_key.split('-')[1],
                                                                                         task_key.split('-')[0])
            cursor.execute(check_sql)
            row = cursor.fetchone()
            if not row or not row[0]:
                key_error = True
            cursor.close()
            conn.close()

    else:
        format_error = True

    return format_error, key_error


def check_message(repo_path, old_rev, new_rev):
    """
     检查提交说明的格式
     JIRA key的真实性
     JIRA title的完整性
    """
    error_format_message = []
    error_key_message = []
    has_error = False

    pattern = r'^<(REQ|BUG|FUNC|DEBUG|PATCH|BASELINE|MERGE)><([A-Z]{2,}\-\d+)><(.+)>$'
    rp = re.compile(pattern)

    if old_rev == '0000000000000000000000000000000000000000' or new_rev == '0000000000000000000000000000000000000000':
        return True
    else:
        status, messages = commands.getstatusoutput('git --git-dir={0} log {1}..{2} '
                                                    '--pretty=format:"%s%b"'.format(repo_path, old_rev, new_rev))
    if status:
        print messages
        sys.exit(1)
    else:
        messages = messages.split('\n')

    for message in messages:
        if message.startswith('Merge') or not message.strip():
            continue
        if message.find('Change-Id:') > 0:
            message = message.split('Change-Id:')[0]
        format_error, key_error = check_message_format(rp, message)
        if format_error:
            error_format_message.append(message)
        if key_error:
            error_key_message.append(message)

    if error_format_message:
        print '\n'.join(
            [
                '=' * 100,
                "\033[43;31;1m推送失败\033[0m",
                "如下提交说明格式书写出错了",
                '\033[43;31;1m' + '\n'.join(error_format_message) + '\033[0m',
                "\033[34m正确的格式是: <REQ|BUG|FUNC|DEBUG|PATCH|BASELINE|MERGE><Jira Key值><提交TAG:描述>\033[0m",
                "\033[32m示例:<REQ><SQZ-22><S9201B_FLY_RU_4.2_V1.1:TP更新>\033[0m",
                "\033[32m示例:<BUG><SZQ-114><S9201B_4.2_V1.1:TP无法使用和升级问题更新>\033[0m",
                '=' * 100
            ])
        has_error = True
    if error_key_message:
        print '\n'.join(
            [
                '=' * 100,
                "\033[43;31;1m推送失败\033[0m\nJIRA_KEY不存在\n",
                '\033[34m如下提交说明中的JIRA_KEY在JIRA数据库中不存在，请填写真实有效的JIRA_KEY\033[0m',
                '\033[43;31;1m' + '\n'.join(error_key_message) + '\033[0m',
                '=' * 100
            ])
        has_error = True
    return not has_error


def repository_push_log(git_path, ref_name, old_rev, new_rev, local_ip, uploader):
    """推送日志记入数据库中便于tsds解析
    """

    date_format = '%Y-%m-%d %H:%M:%S'
    now = time.strftime(date_format, time.localtime())
    commits = []

    if ref_name.find('/for/') > 0:
        push_type = 'change push'
    else:
        push_type = 'update_reference'

    if old_rev == '0000000000000000000000000000000000000000' or new_rev == '0000000000000000000000000000000000000000':
        status, messages = commands.getstatusoutput('git --git-dir={0} log {1} -1 '
                                                    '--pretty=format:%H'.format(git_path, new_rev))
    else:
        status, messages = commands.getstatusoutput('git --git-dir={0} log {1}..{2} '
                                                    '--pretty=format:%H'.format(git_path, old_rev, new_rev))
    commits = [tmp.strip() for tmp in messages.split()]

    try:
        conn = connect(host=db_host, db=db_name, user=db_user, passwd=<PASSWORD>)
        cursor = conn.cursor()
        sql = "INSERT INTO repo_push_history (server_ip, repository_path, reference_name, commits, push_time, " \
              "push_user, push_type, status) VALUES('%s', '%s', '%s', '%s', '%s', '%s', '%s', 0 )" % \
              (local_ip, git_path, ref_name, '/'.join(commits), now, uploader, push_type)

        cursor.execute(sql)
        conn.commit()
        cursor.close()
        conn.close()
    except MySQLdb.Error, e:
        send_mail(["<EMAIL>"], "Tup数据库记录Push history失败_{0}".format(local_ip),
                  'git仓库路径:{0}<br>git分支:{1}<br>更新时间:{2}<br>出错信息:{3}'.format(git_path, ref_name, now, e))


def main():
    des = u"gerrit ref update hooks"
    prog = u"ref-update"
    ver = "%prog v0.0.1"
    usage = "%prog ref-update --project <project name> --refname<refname> --uploader<uploader>  --oldrev<shal> --newrev<shal>"

    p = OptionParser(description=des, prog=prog, version=ver, usage=usage, add_help_option=True)
    p.add_option('--project', action='store', type='string', dest='project', help="Gerrit project name")
    p.add_option('--refname', action='store', type='string', dest='refname', help="git reference name")
    p.add_option('--uploader', action='store', type='string', dest='uploader', help="Gerrit user and email")
    p.add_option('--oldrev', action='store', type='string', dest='oldrev', help="old rev has")
    p.add_option('--newrev', action='store', type='string', dest='newrev', help="new rev has")

    option, args = p.parse_args()
    repository = '/home/android/review-site/repositories/'
    repo_path = option.project
    git_path = os.path.join(repository,repo_path) + '.git'
    old_rev = option.oldrev
    new_rev = option.newrev
    ref_name = option.refname
    uploader = option.uploader
    local_ip = get_ip_address('eth0')
    if local_ip:
        local_ip = get_ip_address('eth1')

    # 检查提交说明信息
    result = check_message(git_path, old_rev, new_rev)
    if result:
        # 添加push数据留着所实时备份
        repository_push_log(git_path, ref_name, old_rev, new_rev, local_ip, uploader)
        # 提交信息加入到rabbitmq队列中，等待后续处理
        if old_rev != '0' * 40 and new_rev != '0' * 40:
            message = {
                'old_rev': old_rev,
                'new_rev': new_rev,
                'ref_name': ref_name,
                'repo_path': repo_path,
                'uploader': uploader,
                'local_ip': local_ip
            }
            push_pubisher(json.dumps(message))
    else:
        sys.exit(1)

if __name__ == '__main__':
    main()
<file_sep>/**
 * Created by changjie.fan on 2016/7/12.
 */
// 项目JIRA任务
var android_project_jira_table = $('#android_project_jira_table').DataTable({
    //dom: "Bfrtip",
    "language": {
        "aria": {
            "sortAscending": ": activate to sort column ascending",
            "sortDescending": ": activate to sort column descending"
        },
        "emptyTable": "No data available in table",
        "info": "Showing _START_ to _END_ of _TOTAL_ records",
        "infoEmpty": "No records found",
        "infoFiltered": "(filtered1 from _MAX_ total records)",
        "lengthMenu": "Show _MENU_ records",
        "search": "Search:",
        "zeroRecords": "No matching records found",
        "paginate": {
            "previous": "Prev",
            "next": "Next",
            "last": "Last",
            "first": "First"
        }
    },
    "bServerSide": true, // save datatable state(pagination, sort, etc) in cookie.
    "bProcessing": true,
    "ajax": {
        url: '/project/project_code_jira_data',
        data: {
                'project_code_id': function () {
                    return $('#project_code_id').val();
                },
                'search_type': function(){
                    return $('#task_search_category').val();
                },
                'search_text': function(){
                    return $('#task_search_text').val();
                }
            }
    },
    "columns": [
        {
            'targets': 0,
            'searchable': false,
            'orderable': false,
            'render': function (data, type, full, meta) {
                return '<div class="checker"><span ><input type="checkbox" checked></span></div>';
            }
        },
        {"data": 'index'},
        {"data": 'task_key'},
        {"data": 'summary'},
        {"data": 'description'},
        {"data": 'category'}
    ],
    "fnRowCallback": function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
        if (aData.category == 'Critical') {
            $('td', nRow).css('background-color', 'Orange');
        }
    },
    "lengthMenu": [
        [5, 20, 100, 200, 500],
        [5, 20, 100, 200, 500] // change per page values here
    ],
    // set the initial value
    "pageLength": 5,
    "pagingType": "bootstrap_full_number"
});

//任务查询
function task_search(){
    $('#android_project_jira_table').DataTable().ajax.reload();
}

// 显示jira任务详细信息
$('#android_project_jira_table').on( 'dblclick', 'tr', function () {
    var row = android_project_jira_table.row($(this)).data();
    window.location.href = "/project/project_branch_task_detail/" + row.id;
});

//jira 任务导出Excel
function export_task_excel(){
    var task_ids = new Array();
    $('#android_project_jira_table  td span').each(function () {
        if ($(this).attr('class') && $(this).attr('class') == 'checked') {
            task_ids.push(android_project_jira_table.row($($($(this).parent()).parent()).parent()).data().id);
        }
    });
    if (task_ids.length == 0){
        bootbox.alert('请至少选中一条记录');
    }else {
        $.ajax({
            url: '/project/jira_task_export_excel',
            type: 'POST',
            data: {'task_ids': task_ids.join(',')},
            success: function (data) {
                window.location.href = '/auth/download_file/?file_path=' + data;
            }
        });
    }
}

// select all
$('#task_select_all').on('click', function () {
    var rows = android_project_jira_table.rows({'search': 'applied'}).nodes();
    if ($('#task_select_all').parent().attr('class') == 'checked') {
        $('input[type="checkbox"]', rows).each(function () {
            $(this).parent().removeClass("checked");
        });
    } else {
        $('input[type="checkbox"]', rows).each(function () {
            $(this).parent().addClass("checked");
        });
    }
});
// 行选择
$('#android_project_jira_table tbody').on('change', 'input[type="checkbox"]', function () {
    if($(this).parent().attr('class') == 'checked'){
        $('#task_select_all').parent().removeClass("checked");
        $(this).parent().removeClass("checked");
    } else {
        $(this).parent().addClass("checked");
    }
});

// 提交commit
var android_project_commit_table = $('#android_project_commit_table').DataTable({
    //dom: "Bfrtip",
    "language": {
        "aria": {
            "sortAscending": ": activate to sort column ascending",
            "sortDescending": ": activate to sort column descending"
        },
        "emptyTable": "No data available in table",
        "info": "Showing _START_ to _END_ of _TOTAL_ records",
        "infoEmpty": "No records found",
        "infoFiltered": "(filtered1 from _MAX_ total records)",
        "lengthMenu": "Show _MENU_ records",
        "search": "Search:",
        "zeroRecords": "No matching records found",
        "paginate": {
            "previous": "Prev",
            "next": "Next",
            "last": "Last",
            "first": "First"
        }
    },
    "bServerSide": true, // save datatable state(pagination, sort, etc) in cookie.
    "bProcessing": true,
    "ajax": {
        url: '/project/project_code_commit_data',
        data: {
                'project_code_id': function () {
                    return $('#project_code_id').val();
                },
                'search_type': function(){
                    return $('#commit_search_category').val();
                },
                'search_text': function(){
                    return $('#commit_search_text').val();
                }
            }
    },
    "columns": [
        {
            'targets': 0,
            'searchable': false,
            'orderable': false,
            'render': function (data, type, full, meta) {
                return '<div class="checker"><span ><input type="checkbox" checked></span></div>';
            }
        },
        {"data": 'index'},
        {"data": 'repo_path'},
        {"data": 'message'},
        {"data": 'commit_email'},
        {"data": 'commit_date'},
        {"data": 'commit_id'}
    ],
    "lengthMenu": [
        [5, 20, 100, 200, 500],
        [5, 20, 100, 200, 500] // change per page values here
    ],
    // set the initial value
    "pageLength": 5,
    "pagingType": "bootstrap_full_number"
});


// 显示commit详细信息
$('#android_project_commit_table').on( 'dblclick', 'tr', function () {
    var row = android_project_commit_table.row($(this)).data();
    window.location.href = "/project/project_branch_commit_detail/" + row.id;
});
//commit 搜索
function commit_search(){
    $('#android_project_commit_table').DataTable().ajax.reload();
}

// select all
$('#commit_select_all').on('click', function () {
    var rows = android_project_commit_table.rows({'search': 'applied'}).nodes();
    if ($('#commit_select_all').parent().attr('class') == 'checked') {
        $('input[type="checkbox"]', rows).each(function () {
            $(this).parent().removeClass("checked");
        });
    } else {
        $('input[type="checkbox"]', rows).each(function () {
            $(this).parent().addClass("checked");
        });
    }
});
// 行选择
$('#android_project_commit_table tbody').on('change', 'input[type="checkbox"]', function () {
    if($(this).parent().attr('class') == 'checked'){
        $('#commit_select_all').parent().removeClass("checked");
        $(this).parent().removeClass("checked");
    } else {
        $(this).parent().addClass("checked");
    }
});

//commit 导出Excel
function export_commit_excel(){
    var commit_ids = new Array();
    $('#android_project_commit_table  td span').each(function () {
        if ($(this).attr('class') && $(this).attr('class') == 'checked') {
            commit_ids.push(android_project_commit_table.row($($($(this).parent()).parent()).parent()).data().id);
        }
    });
    if (commit_ids.length == 0){
        bootbox.alert('请至少选中一条记录');
    }else {
        $.ajax({
            url: '/project/commit_export_excel',
            type: 'POST',
            data: {'commit_ids': commit_ids.join(',')},
            success: function (data) {
                window.location.href = '/auth/download_file/?file_path=' + data;
            }
        });
    }
}




//分支比较
function compare_branch(){
    if($('#first_project_code_id').val() && $('#second_project_code_id').val()) {
        showMask();
        window.location.href = '/project/project_branch_commit_compare/' + $('#first_project_code_id').val() +
            '/' + $('#second_project_code_id').val();
    }
}<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""
from flask import Blueprint

build = Blueprint('build', __name__)
from . import views<file_sep># -*- coding: utf-8 -*-
# Created by changjie.fan on 2016/6/12
"""
Android 项目的模型
"""

from tms import db

project_followers = db.Table('project_android_followers', db.metadata,
                             db.Column('user_id', db.Integer, db.ForeignKey('tms_user.id')),
                             db.Column('project_id', db.Integer, db.ForeignKey('project_android.id'))
                             )


class ProjectAndroid(db.Model):
    """"Android工程项目"""

    __tablename__ = 'project_android'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64))
    project_key = db.Column(db.String(64), nullable=False)
    category = db.Column(db.String(64))
    # Android版本号 N/M/L
    android_version = db.Column(db.String(10))
    # 平台厂商名称
    platform_name = db.Column(db.String(64))
    # 基础名称
    baseline_name = db.Column(db.String(200))

    # 版本在ftp服务器上的路径
    version_ftp_path = db.Column(db.String(255))

    # myos_configs库中ApkVersion.ini文件的路，路径存在表示导入了myos
    myos_config_patch = db.Column(db.String(300))

    # 提交通知邮件
    commit_notice_email = db.Column(db.String(500))

    # 默认货单
    default_project_code = db.Column(db.Integer)

    # 项目状态 开发、DCC、关闭
    status = db.Column(db.String(20))

    # spm
    spm_id = db.Column(db.Integer, db.ForeignKey('tms_user.id'))
    spm = db.relation('User', backref=db.backref('project_spm'), foreign_keys='ProjectAndroid.spm_id')

    # 测试负责人
    tester_id = db.Column(db.Integer, db.ForeignKey('tms_user.id'))
    tester = db.relation('User', backref=db.backref('project_tester'), foreign_keys='ProjectAndroid.tester_id')

    # SCM
    scm_id = db.Column(db.Integer, db.ForeignKey('tms_user.id'))
    scm = db.relation('User', backref=db.backref('project_scm'), foreign_keys='ProjectAndroid.scm_id')

    # 运营商项目才有的字段
    operators = db.Column(db.String(64))

    # 订单项目 出货地点
    customer_area = db.Column(db.String(64))

    # 订单项目可有的客户属性
    customer_name = db.Column(db.String(64))

    # 项目扩展字段
    extended_field = db.Column(db.String(64))

    parent_id = db.Column(db.Integer, db.ForeignKey('project_android.id'), nullable=True)
    parent = db.relation('ProjectAndroid', uselist=False, remote_side=[id], backref=db.backref('children'))

    # 项目关注者
    followers = db.relationship('User', secondary='project_android_followers', backref='project_android_f')

    # 项目创建时间
    create_time = db.Column(db.DateTime)

    # 备注信息
    note = db.Column(db.String(200))

    def __repr__(self):
        return u"<AndroidProject>%s" % self.name


class ProjectAndroidInfo(db.Model):
    """"Android工程项目的关联信息表"""

    __tablename__ = 'project_android_info'

    id = db.Column(db.Integer, primary_key=True)
    jenkins_url = db.Column(db.Text)
    coverity_url = db.Column(db.Text)
    ftp_url = db.Column(db.Text)
    svn_url = db.Column(db.String(255))
    gerrit_url = db.Column(db.String(255))
    push_command = db.Column(db.Text)
    other_command = db.Column(db.Text)
    project_android_id = db.Column(db.Integer, db.ForeignKey('project_android.id'))
    project_android = db.relationship('ProjectAndroid', uselist=False, backref=db.backref('project_info'))

    def __repr__(self):
        return u"<ProjectAndroidInfo>%s" % self.project_android.name


class ProjectAndroidHistory(db.Model):
    """"Android工程项目的修改历史"""
    __tablename__ = 'project_android_history'

    id = db.Column(db.Integer, primary_key=True)
    project_android_id = db.Column(db.Integer, db.ForeignKey('project_android.id'))
    project_android = db.relationship('ProjectAndroid',  backref=db.backref('project_history'))
    operate_time = db.Column(db.DateTime)
    operate_user_id = db.Column(db.Integer, db.ForeignKey('tms_user.id'))
    operate_user = db.relation('User', backref=db.backref('project_android_history'))
    operate_type = db.Column(db.String(10))
    operate_content = db.Column(db.Text)

    def __repr__(self):
        return u"<ProjectAndroidHistory>%s" % self.project_android.name


class ProjectAndroidMemeber(db.Model):
    """项目成员"""

    __tablename__ = 'project_android_memeber'

    id = db.Column(db.Integer, primary_key=True)

    user_id = db.Column(db.Integer, db.ForeignKey('tms_user.id'))
    user = db.relation('User', backref=db.backref('project_member'))

    project_id = db.Column(db.Integer, db.ForeignKey('project_android.id'))
    project = db.relation('ProjectAndroid', backref=db.backref('project_member'))

    # 项目成员的角色SPM/dev_leader/scm/tester_leader
    project_role = db.Column(db.String(64))

    def __repr__(self):
        return u"<ProjectAndroidMemeber>%s" % self.project.name


class ProjectMyOSConfig(db.Model):
    """项目MYOS应用配置表"""

    __tablename__ = 'project_myos_app_config'

    id = db.Column(db.Integer, primary_key=True)
    app_name = db.Column(db.String(64))
    app_version = db.Column(db.String(64))
    overrides = db.Column(db.String(64))
    support = db.Column(db.String(64))

    project_id = db.Column(db.Integer, db.ForeignKey('project_android.id'))
    project = db.relation('ProjectAndroid', backref=db.backref('myos_app'))

    def __repr__(self):
        return u"<ProjectMyOSConfig>%s" % self.app_name


class ProjectMyOSConfigLog(db.Model):
    """项目MYOS应用配置修改日志表"""

    __tablename__ = 'project_myos_app_config_log'

    id = db.Column(db.Integer, primary_key=True)

    config_id = db.Column(db.Integer, db.ForeignKey('project_myos_app_config.id'))
    config = db.relation('ProjectMyOSConfig', backref=db.backref('myos_app_config_log'))

    old_version = db.Column(db.String(64))
    new_version = db.Column(db.String(64))

    # myos_configs仓库中的commit id
    commit_id = db.Column(db.String(40))

    # add/remove/modify
    operate_type = db.Column(db.String(64))
    operate_time = db.Column(db.DateTime)
    operator_id = db.Column(db.Integer, db.ForeignKey('tms_user.id'))
    operator = db.relation('User', backref=db.backref('myos_config_modify_user'))

    def __repr__(self):
        return u"<ProjectMyOSConfigLog>%s" % self.config.app_name


class ProjectVersion(db.Model):
    """项目版本信息表"""

    __tablename__ = 'project_version'

    id = db.Column(db.Integer, primary_key=True)
    version_number = db.Column(db.String(64))

    # 旧的内部版本号
    old_version = db.Column(db.String(64))
    # 新的内部版本号
    new_version = db.Column(db.String(64))
    # 客户版本号
    customer_version = db.Column(db.String(64))
    # 外部的新版本号
    out_new_version = db.Column(db.String(64))
    # 版本目的
    version_purpose = db.Column(db.String(300))
    # 版本修改内容
    version_modify = db.Column(db.Text)
    # ftp服务器上的路径
    remote_ftp_path = db.Column(db.String(300))
    # 上传ftp的文件路径
    ftp_files = db.Column(db.String(300))
    # 编译者邮箱
    user_email = db.Column(db.String(64))
    # 编译人
    user_name = db.Column(db.String(300))
    # 编译时间
    compile_time = db.Column(db.String(64))
    # 项目JIRA_KEY
    project_key = db.Column(db.String(300))
    # 打包文件列表
    package_content = db.Column(db.Text)
    # 版本类型
    version_category = db.Column(db.String(64))
    # 编译结果
    result = db.Column(db.Boolean, default=True)
    # jenkins url
    jenkins_url = db.Column(db.String(255))
    # 变更集中的commit
    commit_change_set = db.Column(db.Text)
    # myos应用版本
    myos_info = db.Column(db.Text)

    # 关联项目
    project_id = db.Column(db.Integer, db.ForeignKey('project_android.id'))
    project = db.relation('ProjectAndroid', backref=db.backref('version'))

    def __repr__(self):
        return u"<ProjectVersion>%s" % self.version_number


class ProjectVersionMyOSConfig(db.Model):
    """项目版本中对应MYOS应用配置表和各应用当前最新版本"""

    __tablename__ = 'project_version_myos_app_config'

    id = db.Column(db.Integer, primary_key=True)

    app_name = db.Column(db.String(64))
    overrides = db.Column(db.String(64))

    current_version = db.Column(db.String(64))
    last_version = db.Column(db.String(64))

    project_version_id = db.Column(db.Integer, db.ForeignKey('project_version.id'))
    project_version = db.relation('ProjectVersion', backref=db.backref('version_myos_app'))

    def __repr__(self):
        return u"<ProjectVersionMyOSConfig>%s" % self.app_name


project_android_code = db.Table('project_android_code', db.metadata,
                                db.Column('project_android_id', db.Integer, db.ForeignKey('project_android.id')),
                                db.Column('project_code_id', db.Integer, db.ForeignKey('project_code.id')),
                                db.Column('description', db.String(64)))


class ProjectCode(db.Model):
    """
    Android 项目的代码信息
    """

    __tablename__ = 'project_code'

    id = db.Column(db.Integer, primary_key=True)
    # 提货单的路径
    manifest_url = db.Column(db.String(255))
    # 货单分支
    manifest_branch = db.Column(db.String(64))
    # 货单名称
    manifest_name = db.Column(db.String(255))
    # 货单仓库
    manifest_repo = db.Column(db.String(255))
    # 代码分支
    branch_name = db.Column(db.String(255))
    # 备份服务器上仓库的路径
    local_repo_path = db.Column(db.String(255))
    # 厂商平台名称
    platform_name = db.Column(db.String(64))

    # 邮件提醒功能的邮件地址，以分号间隔
    commit_notice_email = db.Column(db.String(500))

    # 项目
    project_android = db.relationship('ProjectAndroid', secondary='project_android_code', backref='project_code')

    # 状态 False 已废弃，True正常
    activate = db.Column(db.Boolean, default=True)

    def __repr__(self):
        return u"<ProjectCode>%s" % self.manifest_name


class ProjectBranchCommit(db.Model):
    """
    货单(分支)上的提交记录
    """

    __tablename__ = 'project_branch_commit'

    id = db.Column(db.Integer, primary_key=True)

    # Git 仓库路径
    repo_path = db.Column(db.String(255))
    # id
    commit_id = db.Column(db.String(40), index=True)
    # tree id
    tree_id = db.Column(db.String(40), index=True)
    # auth 姓名
    auth_name = db.Column(db.String(64), index=True)
    # auth 邮箱
    auth_email = db.Column(db.String(64), index=True)
    # auth 时间
    auth_date = db.Column(db.String(64))
    # commit 姓名
    commit_name = db.Column(db.String(64))
    commit_email = db.Column(db.String(64))
    commit_date = db.Column(db.String(64))
    # 提交说明
    message = db.Column(db.String(255))
    # 修改的文件
    change_file = db.Column(db.Text)
    # push者
    submitter = db.Column(db.String(64))

    # 项目JIRA_KE
    project_key = db.Column(db.String(64), index=True)
    # JIRA任务KEY
    task_key = db.Column(db.String(64), index=True)

    # False表示已经此commit已经从分支上删除了
    activate = db.Column(db.Boolean, default=True)

    project_code_id = db.Column(db.Integer, db.ForeignKey('project_code.id'))
    project_code = db.relation('ProjectCode', backref=db.backref('branch_commit'))

    def __repr__(self):
        return u"<ProjectBranchCommit>%s" % self.project_code.manifest_name


class ProjectBranchTask(db.Model):
    """
    项目分支上修改的JIRA任务
    """
    __tablename__ = 'project_branch_task'

    id = db.Column(db.Integer, primary_key=True)

    # jira数据库中issue的id
    issue_id = db.Column(db.Integer)
    # jira任务KEY
    task_key = db.Column(db.String(64), index=True)
    # 任务名称
    summary = db.Column(db.String(255), index=True)
    # 任务描述
    description = db.Column(db.String(255))
    # 任务类型Critical/Public
    category = db.Column(db.String(64))

    # False表示已经此commit已经从分支上删除了
    activate = db.Column(db.Boolean, default=True)

    project_code_id = db.Column(db.Integer, db.ForeignKey('project_code.id'))
    project_code = db.relation('ProjectCode', backref=db.backref('branch_task'))

    def __repr__(self):
        return u"<ProjectBranchTask>%s" % self.task_key


class ProjectBranchTaskCommit(db.Model):
    """
    任务关联的commit
    """

    __tablename__ = 'project_branch_task_commit'

    id = db.Column(db.Integer, primary_key=True)

    project_branch_task_id = db.Column(db.Integer, db.ForeignKey('project_branch_task.id'))
    project_branch_task = db.relation('ProjectBranchTask', backref=db.backref('branch_task_commit'))

    project_branch_commit_id = db.Column(db.Integer, db.ForeignKey('project_branch_commit.id'))
    project_branch_commit = db.relation('ProjectBranchCommit', backref=db.backref('branch_task_commit'))

    def __repr__(self):
        return u"<ProjectBranchTaskCommit>%s" % self.project_branch_task.name


class ProjectBranchMergeCommit(db.Model):
    """
    项目分支上从其他分支合并来的提交
    """

    __tabename__ = 'project_branch_merge_commit'

    id = db.Column(db.Integer, primary_key=True)

    commit_id = db.Column(db.String(40), index=True)

    dest_project_code_id = db.Column(db.Integer, db.ForeignKey('project_code.id'))
    dest_project_code = db.relation('ProjectCode', backref=db.backref('dest_project_code'),
                                    foreign_keys='ProjectBranchMergeCommit.dest_project_code_id')

    src_project_code_id = db.Column(db.Integer, db.ForeignKey('project_code.id'))
    src_project_code = db.relation('ProjectCode', backref=db.backref('src_project_code'),
                                   foreign_keys='ProjectBranchMergeCommit.src_project_code_id')

    meger_author_email = db.Column(db.String(64))

    def __repr__(self):
        return u"<ProjectBranchMergeCommit>%s" % self.project_code.branch_name




class ProjectTag(db.Model):
    """
    项目版本tag
    """

    __tablename__ = 'project_tag'

    id = db.Column(db.Integer, primary_key=True)
    # 版本tag名称
    tag_name = db.Column(db.String(255))

    # False表示已经此tag已经删除了
    activate = db.Column(db.Boolean, default=True)

    project_id = db.Column(db.Integer, db.ForeignKey('project_android.id'))
    project = db.relation('ProjectAndroid', backref=db.backref('project_tag'))

    def __repr__(self):
        return u"<ProjectTag>%s" % self.tag_name


class ProjectTagCommit(db.Model):
        """
        项目版本tag的差异日志
        """

        __tablename__ = 'project_tag_commit'

        id = db.Column(db.Integer, primary_key=True)

        project_tag_id = db.Column(db.Integer, db.ForeignKey('project_tag.id'))
        project_tag = db.relation('ProjectTag', backref=db.backref('project_tag_commit'))

        project_branch_commit_id = db.Column(db.Integer, db.ForeignKey('project_branch_commit.id'))
        project_branch_commit = db.relation('ProjectBranchCommit', backref=db.backref('project_tag_commit'))

        def __repr__(self):
            return u"<ProjectTagCommit>%s" % self.project_tag.tag_name


class CommonIssue(db.Model):
    """项目共性问题"""

    __tablename__ = 'project_common_issue'

    id = db.Column(db.Integer, primary_key=True)

    # 共性问题处理的项目
    project_id = db.Column(db.Integer, db.ForeignKey('project_android.id'))
    project = db.relation('ProjectAndroid', backref=db.backref('project_common_issue'))

    # jira 中 原共性问题处理项目的任务key值
    jira_key = db.Column(db.String(64))

    # 问题名称
    name = db.Column(db.String(255))
    # 问题描述
    description = db.Column(db.String(500))
    # 解决方案
    solutions = db.Column(db.Text)
    # 附件
    attachment = db.Column(db.String(500))
    # 提交问题的测试工程师
    tester_id = db.Column(db.Integer, db.ForeignKey('tms_user.id'))
    tester = db.relation('User', backref=db.backref('common_issue_tester'), foreign_keys='CommonIssue.tester_id')
    submit_issue_time = db.Column(db.DateTime)
    # 提供解决方案的开发工程师
    developer_id = db.Column(db.Integer, db.ForeignKey('tms_user.id'))
    developer = db.relation('User', backref=db.backref('common_issue_developer'), foreign_keys='CommonIssue.developer_id')
    submit_solution_time = db.Column(db.DateTime)
    # 确认问题的spm
    spm_id = db.Column(db.Integer, db.ForeignKey('tms_user.id'))
    spm = db.relation('User', backref=db.backref('common_issue_spm'), foreign_keys='CommonIssue.spm_id')
    # spm确认时间
    confirm_time = db.Column(db.DateTime)
    # 影响范围(影响的项目)
    effect_scope = db.Column(db.Text)
    # 厂商平台
    platform_name = db.Column(db.String(64))
    # 厂商基线
    baseline_name = db.Column(db.String(255))
    # 已处理过的项目
    processed_project = db.Column(db.String(255))
    # 问题状态 1 测试录入问题 -> 2 待开发填写解决方案 -> 3 待spm确认问题 -> 4 Spm已确认 -1 问题取消(删除)
    status = db.Column(db.Integer, default=1)
    # 邮件通知列表
    to_list = db.Column(db.String(500))
    # 问题类别
    category = db.Column(db.String(50))
    # 在涉及项目中新建问题的JIRA_KEY
    effect_project_new_task_key = db.Column(db.Text)
    # 问题来源
    issue_from = db.Column(db.String(50))
    
    def __repr__(self):
        return u"<CommonIssue> %r" % self.name


class ProjectTask(db.Model):
    """项目开发任务(和JIRA任务)"""

    __tablename__ = 'project_task'

    id = db.Column(db.Integer, primary_key=True)

    # jira数据库中issue的id
    issue_id = db.Column(db.Integer)
    # jira任务KEY
    task_key = db.Column(db.String(64), index=True)
    # 任务名称
    summary = db.Column(db.String(255), index=True)
    # 任务描述
    description = db.Column(db.String(255))
    # 任务类型 客户需求、公共需求、缺陷、共性问题、安全补丁等
    category = db.Column(db.String(64))

    project_id = db.Column(db.Integer, db.ForeignKey('project_android.id'))
    project = db.relation('ProjectAndroid', backref=db.backref('project_task'))

    def __repr__(self):
        return u"<ProjectTask>%s" % self.task_key

<file_sep># -*- coding: utf-8 -*-
# Created by changjie.fan on 2016/6/28
"""
将发邮件的任务添加到Rabbitmq队列中
等待消费队列处理
"""
import simplejson as json
import pika


def send_mail_pubisher(message):
    """
    向队列中添加邮件发送任务
    :param message: json格式字串
    :return:
    """

    # 创建连接connection到localhost
    con = pika.BlockingConnection(pika.ConnectionParameters('192.168.10.207'))
    # 创建虚拟连接channel
    cha = con.channel()
    # 创建队列anheng,durable参数为真时，队列将持久化；exclusive为真时，建立临时队列
    result = cha.queue_declare(queue='send_email', durable=True, exclusive=False)
    # 创建名为yanfa,类型为fanout的exchange，其他类型还有direct和topic，如果指定durable为真，exchange将持久化
    cha.exchange_declare(durable=False,
                         exchange='tms',
                         type='direct', )
    # 绑定exchange和queue,result.method.queue获取的是队列名称
    cha.queue_bind(exchange='tms',
                   queue=result.method.queue,
                   routing_key='', )
    # 公平分发，使每个consumer在同一时间最多处理一个message，收到ack前，不会分配新的message
    cha.basic_qos(prefetch_count=1)

    # 消息持久化指定delivery_mode=2；
    cha.basic_publish(exchange='',
                      routing_key='send_email',
                      body=message,
                      properties=pika.BasicProperties(
                          delivery_mode=2,
                      ))
    print '[x] Sent %r' % (message,)

    # 关闭连接
    con.close()

if __name__ == '__main__':
    message = {
        'to_list': '<EMAIL>',
        'sub': 'Rabbitmq',
        'content': 'Test RabbitMQ send Mail'
    }
    send_mail_pubisher(json.dumps(message))
<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

"""测试工程师"""
import os
import json
from datetime import datetime
import commands

from flask import render_template, redirect, url_for, request, flash, current_app, session, send_file
from flask.ext.login import login_user, logout_user, login_required, current_user
from flask.ext.mail import Mail, Message
from functools import update_wrapper

from tms import db, mail
from tms.project.models import Version, VersionHistory, ReleaseEmailAddress
from tms.utils.MyFTP import MyFTP
from tms.utils.UpdateReleaseRepo import update_release_repository
from tms.utils.email_format import TEST_FAILURE_TEMPLATE, TEST_PASS_TEMPLATE, RELEASE_TEMPLATE, \
    DCC_RELEASE_TEMPLATE, SPECIAL_VERSION_TEMPLATE, VERSION_TEST_TEMPLATE
from . import qa
from models import *
from tms.auth.models import User, Role
from tms.utils.SendMailPubisher import send_mail_pubisher


def need_test_version():
    """
    待测试版本的装饰器
    os测试用户登录时显示待测试的版本，其他角色不显示
    """
    def decorator(fn):
        def wrapped_function(*args, **kwargs):
            if session['user']['role_name'] == 'os_test':
                versions = Version.query.filter(Version.status == '测试').order_by(Version.compile_time.desc()).all()
                session['need_test_version_count'] = len(versions)
                session['need_test_version'] = [{"id": version.id, "name": '{0}_{1}'.format(version.name, version.version_number)}for version in versions]
            return fn(*args, **kwargs)
        return update_wrapper(wrapped_function, fn)
    return decorator


@qa.route('/my_version')
@login_required
@need_test_version()
def my_version():
    """ 待我测试的版本 """

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '我测试的版本', 'url': url_for('qa.my_version')})
    session['menu_path'] = ['版本管理', '我测试的版本']

    version_type = request.args.get('version_type', '')
    current_app.logger.info(version_type)
    if version_type == 'need_test':
        versions = Version.query.filter(Version.status == '测试').order_by(Version.compile_time.desc()).all()
    else:
        # 当前用户测试过和待测试的版本
        versions = Version.query.filter(Version.test_user_id == current_user.id).order_by(Version.compile_time.desc()).all()

    return render_template('/qa/version_page.html', role_name=session['user']['role_name'], versions=versions,
                           version_type=version_type)


@qa.route('/all_version')
@login_required
@need_test_version()
def all_version():
    """所有版本"""

    versions = Version.query.order_by(Version.compile_time.desc()).all()

    return render_template('/qa/version_page.html', role_name=session['user']['role_name'], versions=versions)


@qa.route('/version_test/<int:version_id>', methods=['GET', 'POST'])
@login_required
@need_test_version()
def version_test(version_id):
    """版本测试结果"""

    # 显示版本信息
    if request.method == 'GET':
        for index, breadcrumb in enumerate(session['breadcrumbs']):
            if u'版本测试' == breadcrumb['text']:
                session['breadcrumbs'] = session['breadcrumbs'][:index+1]
                break
        else:
            session['breadcrumbs'].append({'text': '版本测试',
                                           'url': url_for('qa.version_test', version_id=version_id)})

        version = Version.query.filter_by(id=version_id).first()
        tasks = []

        # 测试权限
        test_permission = False
        # 发布全新
        release_permission = False

        if session['user']['role_name'] == 'os_test' and version.status == '测试':
            test_permission = True

        if session['user']['role_name'] == 'os_test' and version.status == '测试通过':
            release_permission = True

        if version.tasks:
            tasks = json.loads(version.tasks, strict=False)

        if version.documents and version.documents.strip():
            #documents = [(os.path.basename(tmp), os.path.getsize(tmp)/1024, tmp.replace('/', '<>')) for tmp in
            # version.documents.split('#') if tmp.strip()]
            documents = [(os.path.basename(tmp), '', tmp.replace('/', '<>')) for tmp in version.documents.split('#') if tmp.strip()]
        else:
            documents = []
        version_level = ['Recommend', 'Must']

        return render_template('/qa/version_test.html',
                               documents=documents,
                               version=version,
                               test_permission=test_permission,
                               release_permission=release_permission,
                               tasks=tasks,
                               version_level=version_level,
                               current_page='qa.my_version')
    # 版本测试结果
    elif request.method == 'POST':
        version = Version.query.filter_by(id=version_id).first()
        project = version.project
        version.release_note = request.form.get('release_note', '')
        version.version_issues = request.form.get('version_issues', '')
        version.unresolved_issues = request.form.get('unresolved_issues', '')
        version.testing_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        version.test_user = current_user
        smi = request.form.get('smi', '')
        if smi:
            version.smi = int(smi)
        version.level = request.form.get('level', 'Recommend')
        result = request.form.get('result', '')
        version.special_version.release_path = request.form.get('release_path', '')

        # 测试通过后发送邮件地址
        test_pass_to_email = request.form.get('test_pass_to_email', '')
        if test_pass_to_email:
            project.test_pass_to_email = ';'.join([tmp.strip() for tmp in test_pass_to_email.split(';') if tmp.strip()])

        # 测试失败发送的邮件地址
        test_failure_to_email = request.form.get('test_failure_to_email', '')
        if test_failure_to_email:
            project.test_failure_to_email = ';'.join([tmp.strip() for tmp in test_failure_to_email.split(';') if tmp.strip()])

        # 选择发放版本，默认是全部
        need_release_file = request.form.get('need_release_file', '')
        if need_release_file:
            version.release_file = '#'.join([tmp.strip() for tmp in need_release_file.split('#') if tmp.strip()])
        else:
            version.release_file = version.ftp_file

        # 保存上传的附件
        exits_documents = [tmp.strip() for tmp in version.documents.split('#') if tmp.strip()]
        documents = request.files.getlist("file[]")

        if documents:
            upload_path = os.path.join(current_app.config['UPLOAD_FOLDER'], version.name, version.version_number)
            if not os.path.exists(upload_path):
                os.makedirs(upload_path)
            for document in documents:
                if document.filename:
                    current_app.logger.info(document.filename)
                    file_path = os.path.join(upload_path, document.filename)
                    # 删除已存在的
                    if os.path.exists(file_path) and os.path.isfile(file_path):
                        os.remove(file_path)

                    document.save(file_path)
                    if file_path not in exits_documents:
                        exits_documents.append(file_path)

        version.documents = '#'.join(exits_documents)

        # 通知邮件
        to_list = []
        cc_list = []
        # 版本编译工程师
        to_list.append(version.compile_email)
        # 软件开发负责人
        if project.develop_leader and project.develop_leader.email:
            to_list.append(project.develop_leader.email)
        # 软件的邮件记录
        if project.to_email:
            for email in project.to_email.split(';'):
                if email.strip():
                    cc_list.append(email)

        # 版本历史记录
        version_history = VersionHistory()

        # 测试不通过不能发放
        if result == '-1':
            if project.test_failure_to_email:
                for email in project.test_failure_to_email.split(';'):
                    if email.strip():
                        to_list.append(email)

            version.status = '测试不过'
            version.testing_result = False
            version_history.description = "{0}版本测试结果: 测试失败\n 测试人:{1}\n版本缺陷:{2}\n遗留问题{3}".format(
                version.name, current_user.username, version.version_issues, version.unresolved_issues)
            # 去重复
            to_list = list(set(to_list))
            cc_list = list(set(cc_list))
            msg = Message(
                '[TSDS] %s_%s 版本测试不通过' % (version.name, version.version_number),
                sender='<EMAIL>',
                # recipients=[version.project.test_leader.email, '<EMAIL>', '<EMAIL>']
                recipients=to_list,
                cc=cc_list
                # recipients=['<EMAIL>']
            )
            msg.html = TEST_FAILURE_TEMPLATE.format(version.project.name,
                                                    version.name,
                                                    version.version_number,
                                                    version.level,
                                                    version.version_issues,
                                                    current_user.display_name,
                                                    current_user.email).replace('通讯录', '通 讯 录')
            mail.send(msg)

            flash(u'提交成功', 'info')
        # 版本废弃
        elif result == '4':
            version.status = '已废弃'
            version.version_discard_time = datetime.now()
            version.discard_user_id = current_user.id

            db.session.add(version)

        # 研发版本测试结果通知
        elif result == '5':
            version.status = '测试通过'
            version.test_user_id = current_user.id
            version.testing_result = True
            version.testing_time = datetime.now()

            db.session.add(version)

            # 版本测试通过后需要通知的人员
            if project.test_pass_to_email:
                for email in project.test_pass_to_email.split(';'):
                    if email.strip():
                        to_list.append(email)
            # 配置组
            to_list.append('<EMAIL>')
            for scm in User.query.join(Role).filter(Role.name == 'scm').all():
                cc_list.append(scm.email)

            to_list = list(set(to_list + cc_list))
            content = VERSION_TEST_TEMPLATE.format(project.name,
                                              version.name,
                                              version.version_number,
                                              version.smi,
                                              version.version_issues.replace('<', '&lt;').replace('\n', '<br>'),
                                              version.release_note.replace('<', '&lt;').replace('\n', '<br>'),
                                              current_user.display_name,
                                              current_user.email).replace('通讯录', '通 讯 录')
            message = {
                'to_list': ';'.join([tmp for tmp in to_list if tmp]),
                'sub': '[TSDS] {0}_{1}版本，测试结果'.format(project.name, version.version_number),
                'content': content
            }
            send_mail_pubisher(json.dumps(message))

            version_history.description = "{0}版本测试结果: 测试通过，测试通过\n 测试人:{1}\n版本缺陷:{2}\n遗留问题{3}" \
                                          "\n发布说明{4}\n适配信息{5}\n导入注意事项{6}".format(version.name,
                                                                                 current_user.username,
                                                                                 version.version_issues,
                                                                                 version.unresolved_issues,
                                                                                 version.release_note,
                                                                                 version.adaptation_note,
                                                                                 version.project_import_note)
            flash(u'提交成功', 'info')

        # 测试通过进行版本发放
        else:
            # 版本测试通过后需要通知的人员
            if project.test_pass_to_email:
                for email in project.test_pass_to_email.split(';'):
                    if email.strip():
                        to_list.append(email)
            # 配置组
            to_list.append('<EMAIL>')
            for scm in User.query.join(Role).filter(Role.name == 'scm').all():
                cc_list.append(scm.email)

            version.status = '已发布'
            version.testing_result = True
            version.release_time = datetime.now()
            version.release_user_id = current_user.id
            version.release_note = request.form.get('release_note', '')
            version.adaptation_note = request.form.get('adaptation_note', '')
            version.project_import_note = request.form.get('project_import_note', '')

            project.last_release_time = version.release_time
            project.last_version_number = version.version_number

            if result == '1':
                # version.release_type = '特殊发放'
                # msg = Message(
                #     '[TSDS] {0}_{1} 特殊发放'.format(project.name, version.version_number),
                #     sender='<EMAIL>',
                #     recipients=to_list,
                #     cc=cc_list
                #     # recipients=['<EMAIL>']
                # )
                # if version.documents:
                #     for document in version.documents.split('#'):
                #         if document:
                #             file_name = os.path.basename(document)
                #             with current_app.open_resource(document) as fp:
                #                 msg.attach(file_name.encode("utf-8"),
                #                            current_app.config['MIME_TYPE'][os.path.splitext(file_name)[1]], fp.read())
                #
                # msg.html = SPECIAL_VERSION_TEMPLATE.format(project.name,
                #                                            version.name,
                #                                            version.version_number,
                #                                            version.smi,
                #                                            version.level,
                #                                            version.project_import_note,
                #                                            project.apk_name,
                #                                            project.apk_package,
                #                                            version.special_version.release_path,
                #                                            version.release_note.replace('\n', '<br>'),
                #                                            version.unresolved_issues.replace('\n', '<br>'),
                #                                            current_user.display_name,
                #                                            current_user.email)
                # mail.send(msg)
                content = SPECIAL_VERSION_TEMPLATE.format(project.name,
                                                           version.name,
                                                           version.version_number,
                                                           version.smi,
                                                           version.level,
                                                           version.project_import_note.replace('<', '&lt;'),
                                                           project.apk_name,
                                                           project.apk_package,
                                                           version.special_version.release_path,
                                                           version.release_note.replace('<', '&lt;').replace('\n', '<br>'),
                                                           version.unresolved_issues.replace('<', '&lt;').replace('\n', '<br>'),
                                                           current_user.display_name,
                                                           current_user.email).replace('通讯录', '通 讯 录')
                to_list = list(set(to_list + cc_list))
                message = {
                    'to_list': ';'.join([tmp for tmp in to_list if tmp]),
                    'sub': '[TSDS] {0}_{1} 特殊发放'.format(project.name, version.version_number),
                    'content': content
                }
                send_mail_pubisher(json.dumps(message))

            elif result == '2':
                version.release_type = 'DCC发放'
                # msg = Message(
                #     '[TSDS] {0}_{1}版本，DCC发放'.format(project.name, version.version_number),
                #     sender='<EMAIL>',
                #     recipients=to_list,
                #     cc=cc_list
                #     # recipients=['<EMAIL>']
                # )
                # if version.documents:
                #     for document in version.documents.split('#'):
                #         if document:
                #             file_name = os.path.basename(document)
                #             with current_app.open_resource(document) as fp:
                #                 msg.attach(file_name.encode("utf-8"),
                #                            current_app.config['MIME_TYPE'][os.path.splitext(file_name)[1]], fp.read())
                #     msg.html = DCC_RELEASE_TEMPLATE.format(project.name,
                #                                            version.name,
                #                                            version.version_number,
                #                                            version.smi,
                #                                            version.level,
                #                                            project.apk_name,
                #                                            project.apk_package,
                #                                            version.special_version.release_path,
                #                                            version.release_note.replace('\n', '<br>'),
                #                                            project.name,
                #                                            version.adaptation_note,
                #                                            version.unresolved_issues.replace('\n', '<br>'),
                #                                            current_user.display_name,
                #                                            current_user.email)
                #
                #     mail.send(msg)
                to_list = list(set(to_list + cc_list))
                content = DCC_RELEASE_TEMPLATE.format(project.name,
                                                       version.name,
                                                       version.version_number,
                                                       version.smi,
                                                       version.level,
                                                       project.apk_name,
                                                       project.apk_package,
                                                       version.special_version.release_path,
                                                       version.release_note.replace('<', '&lt;').replace('\n', '<br>'),
                                                       project.name,
                                                       version.adaptation_note.replace('<', '&lt;'),
                                                       version.unresolved_issues.replace('<', '&lt;').replace('\n', '<br>'),
                                                       current_user.display_name,
                                                       current_user.email).replace('通讯录', '通 讯 录')
                message = {
                    'to_list': ';'.join([tmp for tmp in to_list if tmp]),
                    'sub': '[TSDS] {0}_{1}版本，DCC发放'.format(project.name, version.version_number),
                    'content': content
                }
                send_mail_pubisher(json.dumps(message))

            elif result == '3':
                version.release_type = '迭代发放'
                # msg = Message(
                #     '[TSDS] {0}_{1}版本，迭代发放'.format(project.name, version.version_number),
                #     sender='<EMAIL>',
                #     recipients=to_list,
                #     cc=cc_list
                #     # recipients=['<EMAIL>']
                # )
                # if version.documents:
                #     for document in version.documents.split('#'):
                #         if document:
                #             file_name = os.path.basename(document)
                #             with current_app.open_resource(document) as fp:
                #                 msg.attach(file_name.encode("utf-8"),
                #                            current_app.config['MIME_TYPE'][os.path.splitext(file_name)[1]], fp.read())
                # msg.html = RELEASE_TEMPLATE.format(project.name,
                #                                    version.name,
                #                                    version.version_number,
                #                                    version.smi,
                #                                    version.level,
                #                                    project.apk_name,
                #                                    project.apk_package,
                #                                    version.special_version.release_path,
                #                                    version.release_note.replace('\n', '<br>'),
                #                                    version.unresolved_issues.replace('\n', '<br>'),
                #                                    current_user.display_name,
                #                                    current_user.email)
                # mail.send(msg)
                to_list = list(set(to_list + cc_list))
                content = RELEASE_TEMPLATE.format(project.name,
                                                   version.name,
                                                   version.version_number,
                                                   version.smi,
                                                   version.level,
                                                   project.apk_name,
                                                   project.apk_package,
                                                   version.special_version.release_path,
                                                   version.release_note.replace('<', '&lt;').replace('\n', '<br>'),
                                                   version.unresolved_issues.replace('<', '&lt;').replace('\n', '<br>'),
                                                   current_user.display_name,
                                                   current_user.email).replace('通讯录', '通 讯 录')
                message = {
                    'to_list': ';'.join([tmp for tmp in to_list if tmp]),
                    'sub': '[TSDS] {0}_{1}版本，迭代发放'.format(project.name, version.version_number),
                    'content': content
                }
                send_mail_pubisher(json.dumps(message))

            version_history.description = "{0}版本测试结果: 测试通过，已发布\n 测试人:{1}\n版本缺陷:{2}\n遗留问题{3}" \
                                          "\n发布说明{4}\n适配信息{5}\n导入注意事项{6}".format(version.name,
                                                                                 current_user.username,
                                                                                 version.version_issues,
                                                                                 version.unresolved_issues,
                                                                                 version.release_note,
                                                                                 version.adaptation_note,
                                                                                 version.project_import_note)
            flash(u'发布成功', 'info')

        version_history.version_id = version_id
        version_history.option_username = session['user']['username']
        version_history.option_email = session['user']['email']
        version_history.option_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

        db.session.add(version)
        db.session.add(version_history)
        db.session.add(version.special_version)
        db.session.add(project)
        db.session.commit()

        return redirect(url_for('qa.version_test', version_id=version_id))


@qa.route('/download_file/<string:file_path>')
@login_required
def download_file(file_path):
    """所有版本"""

    file_path = file_path.replace('<>', '/')
    return send_file(open(file_path, 'rb'), as_attachment=True, attachment_filename=os.path.basename(file_path))
<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""
from datetime import datetime
import json
import os

from flask import render_template, redirect, url_for, request, flash, current_app, session
from flask.ext.login import login_user, logout_user, login_required, current_user
from flask.ext.mail import Mail, Message
from werkzeug import secure_filename
from functools import update_wrapper

from tms import db, mail
from . import developer
from tms.project.models import Project, Version, VersionHistory, SpecialVersion
from tms.project.androidProjectModules import ProjectAndroid
from tms.auth.models import User, Role
from tms.repository.models import Repository
from tms.utils.email_format import VERSION_SELF_TEST_TEMPLATE
from tms.utils.SendMailPubisher import send_mail_pubisher


def need_self_test_version():
    """
    待开发自测的版本装饰器
    非开发工程师不显示
    """
    def decorator(fn):
        def wrapped_function(*args, **kwargs):
            if session['user']['role_name'] == 'APK开发工程师':
                # 当前用户所有待自测的版本
                versions = Version.query.join(VersionHistory).filter(
                    db.and_(Version.compile_email == current_user.email, Version.status == '自测')).all()
                session['need_test_version_count'] = len(versions)
                session['need_test_version'] = [{"id": version.id, "name": '{0}_{1}'.format(version.name, version.version_number)}for version in versions]
            return fn(*args, **kwargs)
        return update_wrapper(wrapped_function, fn)
    return decorator


@developer.route('/all_project')
@login_required
@need_self_test_version()
def all_project():
    """所有的项目"""

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '所有项目', 'url': url_for('developer.all_project')})
    session['menu_path'] = ['项目管理', '所有项目']

    projects = Project.query.all()
    # 负责的项目
    leader_project = Project.query.filter_by(develop_leader=current_user).all()
    # 关注的项目
    follower_project = Project.query.join(User.project_f).filter(User.id == current_user.id).all()

    result = []
    index = 1
    for project_obj in projects:
        if project_obj in leader_project:
            result.append((index, project_obj, 'leader'))
        elif project_obj in follower_project:
            result.append((index, project_obj, 'start'))
        else:
            result.append((index, project_obj, 'other'))
        index += 1

    # 所有Android项目
    # 已经关注的项目
    follower_android_project = ProjectAndroid.query.join(User.project_android_f).filter(
        User.id == current_user.id).all()
    project_android = []
    all_android_project = ProjectAndroid.query.all()
    for project_obj in all_android_project:
        if project_obj in follower_android_project:
            project_android.append((index, project_obj, 'start', 'android'))
        else:
            project_android.append((index, project_obj, 'other', 'android'))
        index += 1

    return render_template('/developer/developer_page.html',
                           all_project=True,
                           role_name=session['user']['role_name'],
                           projects=result,
                           project_android=project_android
                           )


@developer.route('/all_apk_project_page')
@login_required
@need_self_test_version()
def all_apk_project_page():
    """所有APK的项目"""

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': 'APK项目', 'url': url_for('developer.all_apk_project_page')})
    session['menu_path'] = ['项目管理', 'APK项目']

    projects = Project.query.all()
    # 负责的项目
    leader_project = Project.query.filter_by(develop_leader=current_user).all()
    # 关注的项目
    follower_project = Project.query.join(User.project_f).filter(User.id == current_user.id).all()

    result = []
    index = 1
    for project_obj in projects:
        if project_obj in leader_project:
            result.append((index, project_obj, 'leader'))
        elif project_obj in follower_project:
            result.insert(0, (index, project_obj, 'start'))
        else:
            result.append((index, project_obj, 'other'))
        index += 1

    return render_template('/developer/developer_page.html',
                           all_project=True,
                           role_name=session['user']['role_name'],
                           projects=result)


@developer.route('/os_dev_my_project')
@login_required
@need_self_test_version()
def os_dev_my_project():
    """os 开发工程师我负责的、参与、关注的项目"""

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '我的项目', 'url': url_for('developer.os_dev_my_project')})
    session['menu_path'] = ['项目管理', '我的项目']

    result = []
    index = 1
    # 负责的项目
    leader_project = Project.query.filter_by(develop_leader=current_user).all()
    for project_obj in leader_project:
        result.append((index, project_obj, 'leader'))
        index += 1

    # 关注的APK项目
    follower_project = Project.query.join(User.project_f).filter(User.id == current_user.id).all()
    for project_obj in follower_project:
        if project_obj not in leader_project:
            result.append((index, project_obj, 'start'))
            index += 1

    # 关注的Android项目
    project_android = []
    follower_android_project = ProjectAndroid.query.join(User.project_android_f).filter(User.id == current_user.id).all()
    for project_obj in follower_android_project:
        project_android.append((index, project_obj, 'start', 'android'))
        index += 1

    # current_app.logger.info()

    return render_template('/developer/developer_page.html',
                           role_name=session['user']['role_name'],
                           projects=result,
                           project_android=project_android
                           )


@developer.route('/start_project')
@login_required
def start_project():
    """关注项目"""

    project_id = request.args.get('project_id', '')
    start_option = request.args.get('start_option', '')
    project = Project.query.filter_by(id=project_id).first()
    if start_option == '1':
        project.followers.append(current_user)
    elif start_option == '-1':
        project.followers.remove(current_user)

    db.session.add(project)
    db.session.commit()

    return '{"result":true}'


@developer.route('/add_project', methods=['GET', 'POST'])
@login_required
@need_self_test_version()
def add_project():
    """新增项目"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '添加项目' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:
        session['breadcrumbs'].append({'text': '添加项目', 'url': url_for('developer.add_project')})

    if request.method == 'POST':
        name = request.form['name']
        apk_name = request.form['apk_name']
        apk_package = request.form['apk_package']
        project_key = request.form['project_key']
        category = 'APP'
        develop_leader_id = request.form['develop_leader']
        test_leader_id = request.form.get('test_leader', '')
        status = request.form['status']
        last_version_number = request.form.get('last_version_number', '')
        last_release_time = request.form.get('last_release_time')
        to_email = request.form.get('to_email', '')
        code_repo_id = request.form.get('code_repo', '')
        release_repo_id = request.form.get('release_repo', '')
        test_case_jar_url = request.form.get('test_case_jar_url', '')

        other_project = Project.query.filter(Project.project_key == project_key).first()

        if other_project is not None:
            flash(u'相同JIRA_KEY项目"{0}"已存在'.format(other_project.name), 'danger')
        else:
            project_obj = Project()
            project_obj.name = name
            project_obj.apk_name = apk_name
            project_obj.apk_package = apk_package
            project_obj.category = category
            project_obj.project_key = project_key
            project_obj.test_case_jar_url = test_case_jar_url
            project_obj.develop_leader = User.query.filter_by(id=develop_leader_id).first()
            project_obj.test_leader = User.query.filter_by(id=test_leader_id).first()
            project_obj.status = status
            project_obj.last_version_number = last_version_number
            project_obj.last_release_time = last_release_time
            project_obj.to_email = to_email.strip().strip('\n')

            db.session.add(project_obj)
            db.session.commit()

            special_version = SpecialVersion()
            special_version.project = project_obj
            special_version.name = 'ODM'
            special_version.ftp_path = os.path.join('ftp://htsvr2/SW_PRJ/app', project_obj.name, 'ODM')
            special_version.svn_path = os.path.join('https://version2.tinno.com/svn/release/APP', project_obj.name, 'ODM')

            if code_repo_id:
                special_version.code_repo_id = code_repo_id
            if release_repo_id:
                special_version.release_repo_id = release_repo_id

            db.session.add(special_version)
            db.session.commit()

            flash(u'新增成功!', 'info')
            return redirect(url_for('developer.all_project'))

    # 超级管理员和配置管理员也可以编辑项目信息且能够编辑仓库信息
    is_super_man = False
    if session['user']['role_name'] == 'admin' or session['user']['role_name'] == 'scm':
        is_super_man = True

    developer_users = []
    test_users = []
    if is_super_man:
        users = User.query.join(User.role).filter(db.or_(Role.name == 'APK开发工程师', Role.name == 'os_test')).all()
        for user in users:
            if user.role.name == 'APK开发工程师':
                developer_users.append(user)
            else:
                test_users.append(user)

    code_repo_list = Repository.query.filter(Repository.category == 2).all()
    release_repo_list = Repository.query.filter(Repository.category == 3).all()

    # 项目状态
    project_category = ['MTK', 'QCM', 'RDA', 'APP']
    # 项目类
    project_status = ['开发中', '迭代发布中', '已关闭', '已暂停']

    return render_template('/developer/project_add.html',
                           project=Project(),
                           is_super_man=is_super_man,
                           developer_users=developer_users,
                           test_users=test_users,
                           code_repo_list=code_repo_list,
                           release_repo_list=release_repo_list,
                           project_category=project_category,
                           project_status=project_status)


@developer.route('/edit_project/<int:project_id>', methods=['GET', 'POST'])
@login_required
@need_self_test_version()
def edit_project(project_id):
    """编辑"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '编辑项目' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:
        session['breadcrumbs'].append({'text': '编辑项目',
                                       'url': url_for('developer.edit_project', project_id=project_id)})

    project_obj = Project.query.filter_by(id=project_id).first()

    if request.method == 'POST':
        name = request.form['name']
        apk_name = request.form['apk_name']
        apk_package = request.form['apk_package']
        project_key = request.form['project_key']
        category = 'APP'
        develop_leader_id = request.form['develop_leader']
        test_leader_id = request.form.get('test_leader', '')
        status = request.form['status']
        last_version_number = request.form.get('last_version_number', '')
        last_release_time = request.form.get('last_release_time')
        to_email = request.form.get('to_email', '')
        test_case_jar_url = request.form.get('test_case_jar_url', '')

        other_project = Project.query.filter_by(name=name).first()

        if other_project is not None and other_project.id != project_id:
            flash(u'相同名称的项目已存在', 'danger')
        else:
            project_obj.name = name
            project_obj.apk_name = apk_name
            project_obj.apk_package = apk_package
            project_obj.category = category
            project_obj.project_key = project_key
            project_obj.test_case_jar_url = test_case_jar_url
            project_obj.develop_leader = User.query.filter_by(id=develop_leader_id).first()
            project_obj.test_leader = User.query.filter_by(id=test_leader_id).first()
            project_obj.status = status
            project_obj.last_version_number = last_version_number
            project_obj.last_release_time = last_release_time
            project_obj.to_email = ';'.join([tmp.strip() for tmp in to_email.strip().split(';') if tmp.strip()])

            db.session.add(project_obj)
            db.session.commit()

            flash(u'修改成功!', 'info')
            return redirect(url_for('developer.all_project'))

    # 只有是项目开发负责才能编辑项目信息
    is_developer_leader = False
    if project_obj.develop_leader == current_user:
        is_developer_leader = True

    # 超级管理员和配置管理员也可以编辑项目信息且能够编辑仓库信息
    is_super_man = False
    if session['user']['role_name'] == 'admin' or session['user']['role_name'] == 'scm':
        is_super_man = True

    developer_users = []
    test_users = []
    if is_developer_leader or is_super_man:
        users = User.query.join(User.role).filter(db.or_(Role.name == 'APK开发工程师', Role.name == 'os_test')).all()
        for user in users:
            if user.role.name == 'APK开发工程师':
                developer_users.append(user)
            else:
                test_users.append(user)

    # 项目最新的版本
    last_version = Version.query.filter(db.and_(Version.project_id == project_obj.id,
                                                Version.version_number == project_obj.last_version_number)).first()
    last_version_id = ''
    if last_version:
        last_version_id = last_version.id

    # 项目的发布对象(版本对象)
    special_versions = SpecialVersion.query.filter(SpecialVersion.project_id == project_obj.id).all()

    # 项目状态
    project_category = ['MTK', 'QCM', 'RDA', 'APP']
    # 项目类
    project_status = ['开发中', '迭代发布中', '已关闭', '已暂停']

    return render_template('/developer/project_edit.html',
                           project=project_obj,
                           last_version_id=last_version_id,
                           special_versions=special_versions,
                           is_super_man=is_super_man,
                           is_developer_leader=is_developer_leader,
                           developer_users=developer_users,
                           test_users=test_users,
                           project_status=project_status,
                           project_category=project_category)


@developer.route('/version_self_test/<int:version_id>', methods=['GET', 'POST'])
@login_required
@need_self_test_version()
def version_self_test(version_id):
    """自测版本的详细信息"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if u'版本信息' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:
        session['breadcrumbs'].append({'text': u'版本信息',
                                       'url': url_for('developer.version_self_test', version_id=version_id)})

    version = Version.query.filter_by(id=version_id).first()
    # 显示版本信息
    if request.method == 'GET':
        tasks = []
        if version.tasks:
            tasks = json.loads(version.tasks, strict=False)

        # 自测权限
        permission = False
        # 版本编译者和项目开发负责人有自测权限
        if version.compile_email == session['user']['email'] and version.status == '自测':
            permission = True
        elif version.project.develop_leader_id == current_user.id and version.status == '自测':
            permission = True

        if version.documents and version.documents.strip():
            documents = [(os.path.basename(tmp), os.path.getsize(tmp)/1024, tmp.replace('/', '<>')) for tmp in version.documents.split('#') if tmp]
        else:
            documents = []
        version_level = ['Recommend', 'Must']
        return render_template('/developer/version_self_test.html',
                               version=version,
                               documents=documents,
                               permission=permission,
                               tasks=tasks,
                               version_level=version_level)
    # 版本自测结果
    else:
        version_history = VersionHistory.query.filter(VersionHistory.version_id == version.id).order_by(VersionHistory.compile_time.desc()).all()[0]

        # 自测试结果
        result = request.form.get('result', '')

        # 版说明
        version.version_note = request.form.get('version_note', '').replace('通讯录', '通u讯u录')

        # 待发布的文件
        need_release_file = request.form.get('need_release_file', '')
        if request.form.get('need_release_file', ''):
            version.release_file = '#'.join([tmp for tmp in need_release_file.split('#') if tmp.strip()])
        else:
            version.release_file = version.ftp_file

        # 保存上传的附件
        if version.documents:
            exits_documents = [tmp.strip() for tmp in version.documents.split('#') if tmp.strip()]
        else:
            exits_documents = []

        documents = request.files.getlist("file[]")

        if documents:
            upload_path = os.path.join(current_app.config['UPLOAD_FOLDER'], version.name, version.version_number)
            if not os.path.exists(upload_path):
                os.makedirs(upload_path)
            for document in documents:
                if document.filename:
                    current_app.logger.info(document.filename)
                    file_path = os.path.join(upload_path, document.filename)
                    # 删除已存在的
                    if os.path.exists(file_path) and os.path.isfile(file_path):
                        os.remove(file_path)

                    document.save(file_path)
                    if file_path not in exits_documents:
                        exits_documents.append(file_path)

        version.documents = '#'.join(exits_documents)

        if result == '1':
            project = version.project
            version.status = '测试'
            version.level = request.form.get('level', 'C')
            version_history.description = "{0}版本 自测通过".format(version.name)
            to_list = ['<EMAIL>', version.compile_email]
            cc_list = []

            # 测试负责人
            if version.project.test_leader and version.project.test_leader.email:
                to_list.append(version.project.test_leader.email)
            else:
                to_list.append(session['user']['email'])

            # 软件开发负责人
            if version.project.develop_leader and version.project.develop_leader.email:
                to_list.append(version.project.develop_leader.email)

            # 版本发布通知邮件
            if version.project.to_email:
                for email in version.project.to_email.split(';'):
                    if email.strip():
                        to_list.append(email.strip())

            # APK的版本发布邮件通知有的OS测试人员，由其内部进行测试任务的分配
            for qa in User.query.join(Role).filter(Role.name == 'os_test').all():
                to_list.append(qa.email)

            # 配置组
            for scm in User.query.join(Role).filter(Role.name == 'scm').all():
                to_list.append(scm.email)

            followers = project.followers
            if followers:
                for follower in followers:
                    cc_list.append(follower.email)

            # 去重复
            cc_list.append('<EMAIL>')
            to_list = list(set(to_list + cc_list))
            tasks = []
            # msg = Message(
            #     u'[TSDS] %s_%s 版本需要测试' % (version.name, version.version_number),
            #     sender='<EMAIL>',
            #     # recipients=[version.project.test_leader.email, '<EMAIL>', '<EMAIL>']
            #     recipients=to_list,
            #     cc=cc_list
            # )

            # if exits_documents:
            #     for document in exits_documents:
            #         file_name = os.path.basename(document)
            #         with current_app.open_resource(document) as fp:
            #             msg.attach(file_name.encode("utf-8"), current_app.config['MIME_TYPE'][os.path.splitext(file_name)[1]], fp.read())
            if version.tasks:
                tasks = ['<table><thead><th>项目名称</th><th>JIRA_KEY</th><th>任务标题</th><th>任务创建人</th></thead><tbody>']
                for task in json.loads(version.tasks, strict=False):
                    tasks.append(
                        '<tr>'
                        '<td>{0}</td>'
                        '<td><a href="http://jira.tinno.com:8080/browse/{1}">{2}</a></td>'
                        '<td>{3}</td>'
                        '<td>{4}</td>'
                        '</tr>'.format(task['project'], task['jira_key'], task['jira_key'], task['title'],
                                       task['creator']))
                tasks.append('</tbody></table>')

            # msg.html = VERSION_SELF_TEST_TEMPLATE.format(project.name, version.name, version.version_number,
            #                                              version.level, version.ftp_path, version.jenkins_url,
            #                                              version.version_note, '\n'.join(tasks),
            #                                              current_user.display_name, current_user.email)
            # mail.send(msg)
            content = VERSION_SELF_TEST_TEMPLATE.format(project.name, version.name, version.version_number,
                                                         version.level, version.ftp_path, version.jenkins_url,
                                                         version.version_note, '\n'.join(tasks),
                                                         current_user.display_name, current_user.email)
            message = {
                'to_list': ';'.join([tmp for tmp in to_list if tmp]),
                'sub': u'[TSDS] %s_%s 版本需要测试' % (version.name, version.version_number),
                'content': content.replace('通讯录', '通 讯 录')
            }
            send_mail_pubisher(json.dumps(message))

        else:
            version.status = '自测不过'
            version_history.description = "{0}版本 自测失败\n".format(version.name)

        version_history.version_id = version_id
        version_history.self_test_user = current_user
        if request == '1':
            version_history.self_test_result = True
        else:
            version_history.self_test_result = False

        version_history.self_test_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

        db.session.add(version)
        db.session.add(version_history)
        db.session.commit()

        return redirect(url_for('developer.my_version'))


@developer.route('/version_commit/<int:version_id>')
@login_required
def version_commit(version_id):
    """版本的commit信息"""
    version = Version.query.filter_by(id=version_id).first()
    if version.commits:
        commits = json.loads(version.commits.replace('<', '&lt;').replace('>', '&gt;'), strict=False)
    else:
        commits = ''
    return json.dumps({'data': commits})


@developer.route('/my_version')
@login_required
@need_self_test_version()
def my_version():
    """
    我编译的版本
    """

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '我的版本', 'url': url_for('developer.my_version')})
    session['menu_path'] = ['项目管理', '我的版本']

    # 显示所有待自测的版本
    version_type = request.args.get('version_type', '')
    if version_type == 'need_test':
        versions = Version.query.filter(
            db.and_(Version.compile_email == session['user']['email'],
                    Version.status == '自测')).order_by(Version.compile_time.desc()).all()
    else:
        # 所有自己编译过的版本
        versions = Version.query.filter(Version.compile_email == session['user']['email']).order_by(Version.compile_time.desc()).all()

    return render_template('/developer/version_page.html',
                           role_name=session['user']['role_name'],
                           versions=versions,
                           version_type=version_type)


@developer.route('/project_version/<int:project_id>', methods=['GET', 'POST'])
@login_required
@need_self_test_version()
def project_version(project_id):
    """项目的所有版本"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if u'项目版本' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:
        session['breadcrumbs'].append({'text': u'项目版本',
                                       'url': url_for('developer.project_version', project_id=project_id)})

    project_special_versions = []
    for special_version in SpecialVersion.query.filter(SpecialVersion.project_id == project_id).all():
        project_special_versions.append(special_version)

    return render_template('/developer/project_version_page.html',
                           project=Project.query.filter_by(id=project_id).first(),
                           project_special_versions=project_special_versions)


@developer.route('/project_version_data')
@login_required
@need_self_test_version()
def project_version_data():

    start = int(request.args.get('start', 0))
    length = int(request.args.get('length', 10))

    project_id = request.args.get('project_id')
    special_version_id = request.args.get('special_version_id')

    version_sql = "SELECT p_v.id, p_v.version_number, p_v.status, p_v.compile_username, p_v.compile_time, " \
                  "p_v.ftp_path, p_v.svn_path, p_s_v.name, p_v.category FROM tms_project_version AS p_v JOIN " \
                  "tms_project_special_version AS p_s_v ON p_v.special_version_id=p_s_v.id JOIN tms_project AS " \
                  "pro ON p_s_v.project_id=pro.id WHERE pro.id={0} AND p_s_v.id={1} " \
                  "ORDER BY p_v.compile_time DESC".format(project_id, special_version_id)

    all_version = db.session.execute(version_sql).fetchall()
    versions = []
    index = start + 1

    for record in all_version[start:start+length]:
        recpord = {}

        recpord['index'] = index
        recpord['id'] = record[0]
        recpord['version_number'] = record[1]
        recpord['status'] = record[2]
        recpord['compile_username'] = record[3]
        recpord['compile_time'] = str(record[4])
        recpord['ftp_path'] = record[5]
        recpord['svn_path'] = record[6]
        recpord['special_version'] = record[7]
        recpord['category'] = record[8]

        versions.append(recpord)
        index += 1

    return '{"recordsTotal": %s ,"recordsFiltered": %s, "data":%s}' % (len(all_version), len(all_version),
                                                                      json.dumps(versions))


@developer.route('/all_version')
@login_required
@need_self_test_version()
def all_version():
    """所有负责项目、参与项目、关注项目的版本"""

    follower_project = Project.query.join(User.project_f).filter(User.id == current_user.id).all()
    leader_project = Project.query.filter(Project.develop_leader_id == current_user.id).all()

    project_id = []
    for project_obj in follower_project:
        project_id.append(project_obj.id)
    for project_obj in leader_project:
        project_id.append(project_obj.id)

    versions = Version.query.filter(Version.project_id.in_(project_id)).order_by(Version.compile_time.desc())

    return render_template('/developer/version_page.html', versions=versions)


@developer.route('/start_project_version')
@login_required
@need_self_test_version()
def start_project_version():
    """所有关注项目的版本"""

    follower_project = Project.query.join(User.project_f).filter(User.id == current_user.id).all()
    versions = Version.query.filter(Version.project_id.in_([project_obj.id for project_obj in follower_project])).order_by(Version.compile_time.desc()).all()

    return render_template('/developer/version_page.html', versions=versions)


@developer.route('/delete_document')
@login_required
def delete_document():
    """删除附件"""

    version_id = request.args.get('version_id', '')
    document_path = request.args.get('document_path', '').replace('<>', '/')

    version = Version.query.filter_by(id=version_id).first()
    documents = version.documents.split('#')
    result = []
    for document in documents:
        if document == document_path:
            continue
        else:
            result.append(document)
    version.documents = '#'.join(result)
    db.session.add(version)
    db.session.commit()

    return '{"result": true}'
<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""
from datetime import datetime
import json
import os

from flask import render_template, redirect, url_for, request, flash, current_app, session
from flask.ext.login import login_user, logout_user, login_required, current_user
from flask.ext.mail import Mail, Message
from werkzeug import secure_filename

from tms import db, mail
from . import scm
from tms.project.models import Project, Version, VersionHistory, SpecialVersion, ReleaseEmailAddress
from tms.project.androidProjectModules import ProjectAndroid
from tms.auth.models import User, Role
from tms.repository.models import Repository, GitServeBackupLog


@scm.route('/all_project')
@login_required
def scm_page():
    """所有的项目"""

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '所有项目', 'url': url_for('scm.scm_page')})
    session['menu_path'] = ['项目管理', '所有项目']

    projects = Project.query.all()
    # 负责的项目
    leader_project = Project.query.filter_by(develop_leader=current_user).all()
    # 关注的项目
    follower_project = Project.query.join(User.project_f).filter(User.id == current_user.id).all()

    result = []
    index = 1
    for project_obj in projects:
        if project_obj in leader_project:
            result.append((index, project_obj, 'leader'))
        elif project_obj in follower_project:
            result.append((index, project_obj, 'start'))
        else:
            result.append((index, project_obj, 'other'))

        index += 1

    # 所有Android项目
    # 已经关注的项目
    follower_android_project = ProjectAndroid.query.join(User.project_android_f).filter(
        User.id == current_user.id).all()
    project_android = []
    all_android_project = ProjectAndroid.query.all()
    for project_obj in all_android_project:
        if project_obj in follower_android_project:
            project_android.append((index, project_obj, 'start', 'android'))
        else:
            project_android.append((index, project_obj, 'other', 'android'))
        index += 1

    return render_template('/developer/developer_page.html',
                           all_project=True,
                           role_name=session['user']['role_name'],
                           projects=result,
                           project_android=project_android
                           )


@scm.route('/all_version')
@login_required
def all_version():
    """所有负责项目、参与项目、关注项目的版本"""

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': u'我的版本', 'url': url_for('scm.all_version')})
    session['menu_path'] = [u'版本管理', u'我的版本']

    versions = Version.query.all()

    return render_template('/developer/version_page.html', versions=versions)


@scm.route('/add_special_version/<int:project_id>', methods=['GET', 'POST'])
@login_required
def add_special_version(project_id):
    """新增或编辑项目版本类型"""

    project_obj = Project.query.filter(Project.id == project_id).first()
    # 系统管理员和配置管理员才能修改项目的源码及发布仓库地址

    code_repo_list = Repository.query.filter(Repository.category == 2).all()
    release_repo_list = Repository.query.filter(Repository.category == 3).all()

    if request.method == 'POST':
        name = request.form.get('name')
        description = request.form.get('description')
        ftp_path = request.form.get('ftp_path')
        svn_path = request.form.get('svn_path')
        code_repo = request.form.get('code_repo')
        release_repo = request.form.get('release_repo')
        other_special_version = SpecialVersion.query.filter(
            db.and_(SpecialVersion.name == name, SpecialVersion.project_id == project_id)).first()
        if other_special_version:
            flash(u'同名版本类型已存在', 'danger')
        else:
            special_version = SpecialVersion()
            special_version.name = name
            special_version.description = description
            special_version.ftp_path = ftp_path
            special_version.svn_path = svn_path
            special_version.code_repo = Repository.query.filter(Repository.id == code_repo).first()
            special_version.release_repo = Repository.query.filter(Repository.id == release_repo).first()
            special_version.project = project_obj
            db.session.add(special_version)
            db.session.commit()

            return redirect(url_for('developer.edit_project', project_id=project_id))

    return render_template('/scm/edit_special_version.html',
                           project=project_obj,
                           special_version=None,
                           code_repo_list=code_repo_list,
                           special_version_id=0,
                           release_repo_list=release_repo_list
                           )


@scm.route('/edit_special_version/<int:special_version_id>', methods=['GET', 'POST'])
@login_required
def edit_special_version(special_version_id):
    """新增或编辑项目版本类型"""

    special_version = SpecialVersion.query.filter(SpecialVersion.id == special_version_id).first()
    project_obj = special_version.project

    # 系统管理员和配置管理员才能修改项目的源码及发布仓库地址

    code_repo_list = Repository.query.filter(Repository.category == 2).all()
    release_repo_list = Repository.query.filter(Repository.category == 3).all()
    # for repo in code_repo_list:
    #     repo.repo_url = '{0}:{1}/{2}'.format(repo.platform.server.remote_name, repo.platform.name, repo.name)
    #
    # for repo in release_repo_list:
    #     repo.repo_url = '{0}:{1}/{2}'.format(repo.platform.server.remote_name, repo.platform.name, repo.name)

    if request.method == 'POST':
        name = request.form.get('name')
        app_name = request.form.get('app_name')
        description = request.form.get('description')
        ftp_path = request.form.get('ftp_path')
        svn_path = request.form.get('svn_path')
        code_repo = request.form.get('code_repo')
        release_repo = request.form.get('release_repo')
        other_special_version = SpecialVersion.query.filter(
            db.and_(SpecialVersion.name == name, SpecialVersion.project_id == project_obj.id)).first()
        if other_special_version and other_special_version.id != special_version_id:
            flash(u'同名版本类型已存在', 'danger')
        else:
            special_version.name = name
            special_version.app_name = app_name
            special_version.description = description
            special_version.ftp_path = ftp_path
            special_version.svn_path = svn_path
            special_version.code_repo = Repository.query.filter(Repository.id == code_repo).first()
            special_version.release_repo = Repository.query.filter(Repository.id == release_repo).first()

            db.session.add(special_version)
            db.session.commit()
            return redirect(url_for('developer.edit_project', project_id=project_obj.id))

    return render_template('/scm/edit_special_version.html',
                           project=project_obj,
                           special_version=special_version,
                           special_version_id=special_version.id,
                           code_repo_list=code_repo_list,
                           release_repo_list=release_repo_list
                           )


@scm.route('/delete_special/<int:special_version_id>', methods=['GET', 'POST'])
@login_required
def delete_special(special_version_id):
    """删除特殊版本"""

    special_version = SpecialVersion.query.filter(SpecialVersion.id == special_version_id).first()
    project = special_version.project
    db.session.delete(special_version)
    db.session.commit()

    flash(u'删除成功!', 'info')

    return redirect(url_for('developer.edit_project', project_id=project.id))


@scm.route('/release_email_address', methods=['GET', 'POST'])
@login_required
def release_email_address():
    """维护版本发布通知邮件地址"""

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '版本发布通知邮件地址', 'url': url_for('scm.release_email_address')})
    session['menu_path'] = ['项目管理', '版本发布通知邮件地址']

    release_email_address_obj = ReleaseEmailAddress.query.first()
    email_address = request.form.get('email_address', '')

    if request.method == 'POST':
        if release_email_address_obj:
            release_email_address_obj.email_address = '\n'.join([tmp.strip() for tmp in email_address.split('\n') if tmp.strip()])
            db.session.add(release_email_address_obj)
            db.session.commit()
        else:
            release_email_address_obj = ReleaseEmailAddress()
            release_email_address.email_address = email_address
            db.session.add(release_email_address_obj)
            db.session.commit()

    return render_template('/scm/release_email_address_edit.html', release_email_address=release_email_address_obj)


@scm.route('/git_backup_log_page', methods=['GET', 'POST'])
@login_required
def git_backup_log_page():
    """Git服务器备份记录页面"""

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '代码管理', 'url': url_for('scm.git_backup_log_page')})
    session['menu_path'] = ['代码管理', '备份管理']

    return render_template('/scm/git_backup_log.html')


@scm.route('/git_backup_log_data', methods=['GET', 'POST'])
@login_required
def git_backup_log_data():
    """Git服务器备份记录数据"""
    start = int(request.args.get('start', 0))
    length = int(request.args.get('length', 10))

    all_count = GitServeBackupLog.query.count()
    records = []
    index = start + 1
    for record in GitServeBackupLog.query.order_by(GitServeBackupLog.backup_time.desc()).all()[start: start+length]:
        records.append({
            "id": record.id,
            "index": index,
            "server_ip": record.server_ip,
            "platform_path": record.platform_path,
            "backup_time": str(record.backup_time),
            "result": record.result,
        })
        index += 1

    return '{"recordsTotal": %s ,"recordsFiltered": %s,"data":%s}' % (all_count, all_count, json.dumps(records))


@scm.route('/git_backup_log_detail_info/<int:log_id>', methods=['GET', 'POST'])
@login_required
def git_backup_log_detail_info(log_id):
    """Git服务器备份记录详细信息"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if u'备份详情' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:
        session['breadcrumbs'].append({'text': u'备份详情',
                                       'url': url_for('scm.git_backup_log_detail_info', log_id=log_id)})

    log_record = GitServeBackupLog.query.filter(GitServeBackupLog.id == log_id).first()

    return render_template('scm/git_backup_log_detail_info.html', log_record_info=log_record.info.replace('\\n', '<br>'))
<file_sep>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Created by changjie.fan on 2016/6/12
"""
将平台化myos的项目配置信息导入数据库中
"""

import sys
import os
from ConfigParser import ConfigParser

from MySQLdb import connect


# myos配置数据库信息
db_host = '192.168.33.7'
db_user = 'tup'
db_passwd = 'tup'
db_name = 'tms'


def add_project(project_name, parent_id, myos_config_patch):
    """项目加入数据库中"""

    con = connect(host=db_host, db=db_name, user=db_user, passwd=db_passwd, charset='utf8')
    cursor = con.cursor()

    if parent_id:
        insert_project = "INSERT INTO project_android(name, category, parent_id, myos_config_patch) VALUES (%s, %s, %s, %s)"
        cursor.executemany(insert_project, [(project_name, u'订单项目', parent_id, myos_config_patch)])
    else:
        insert_project = "INSERT INTO project_android(name, category, myos_config_patch) VALUES (%s, %s, %s)"
        cursor.executemany(insert_project, [(project_name, u'产品项目', myos_config_patch)])
    con.commit()

    select_project = "SELECT id FROM project_android WHERE myos_config_patch='%s'" % myos_config_patch
    print select_project
    cursor.execute(select_project)
    project_id = str(cursor.fetchone()[0])
    con.close()

    return project_id


def get_parent_project(myos_config_patch):
    """获取父项目的记录"""

    con = connect(host=db_host, db=db_name, user=db_user, passwd=db_passwd, charset='utf8')
    cursor = con.cursor()
    select_project = "SELECT id FROM project_android WHERE myos_config_patch='%s' AND category='产品项目'" % myos_config_patch
    cursor.execute(select_project)
    project_id = cursor.fetchone()
    if project_id:
        project_id = project_id[0]
    con.close()

    return project_id



def add_config(project_id, config_path):
    """解析ApkVersion将其加入数据库中"""

    cp = ConfigParser()
    cp.read(config_path)
    app_config = []
    for app_name in cp.sections():
        # print app_name
        if app_name == 'ProjectInfo':
            continue

        if cp.get(app_name, 'support') == 'yes':
            support = 'yes'
        else:
            support = 'no'

        if cp.has_option(app_name, 'overrides') and cp.get(app_name, 'overrides') and cp.get(app_name, 'overrides') != 'null':
            overrides = cp.get(app_name, 'overrides')
        else:
            overrides = ''

        app_config.append((app_name, support, cp.get(app_name, 'version').strip(), overrides, project_id))

    insert_myos_config = "INSERT INTO project_myos_app_config(app_name, support, app_version, overrides, project_id)" \
                         "VALUES (%s, %s, %s, %s, %s)"

    con = connect(host=db_host, db=db_name, user=db_user, passwd=db_passwd, charset='utf8')
    cursor = con.cursor()
    cursor.executemany(insert_myos_config, app_config)
    con.commit()
    con.close()


def main(config_repo_path):
    """递归config仓库的所有目录找出ApkVersion.ini文件，然后解析ApkVersion.ini将其随产品名称、项目名称
        一起添加进数据库
    """

    project_config = []
    for root, dirs, files in os.walk(config_repo_path):
        for file_name in files:
            if file_name == 'ApkVersion.ini':
                project_config.append(os.path.join(root, file_name))

    for project_config_path in project_config:
        apk_version_path = project_config_path.split(config_repo_path)[1].strip('/')
        if apk_version_path.find('trunk') > 0:
            # 添加项目记录
            product_name = [tmp for tmp in apk_version_path.split('/') if tmp.strip()][0]
            parent_id = add_project(product_name, '', apk_version_path)
            # 解析配置文件
            add_config(parent_id, project_config_path)
        elif apk_version_path.find('SUB_PROJECTS') > 0:
            product_name = [tmp for tmp in apk_version_path.split('/') if tmp.strip()][0]
            project_name = [tmp for tmp in apk_version_path.split('/SUB_PROJECTS/')[1].split('/') if tmp.strip()][0]
            parent_id = get_parent_project(os.path.join(apk_version_path.split('/SUB_PROJECTS/')[0], 'ApkVersion.ini'))
            project_id = add_project(project_name, parent_id, apk_version_path)
            add_config(project_id, project_config_path)
        else:
            # 添加项目记录
            product_name = [tmp for tmp in apk_version_path.split('/') if tmp.strip()][0]
            project_name = [tmp for tmp in apk_version_path.split('/') if tmp.strip()][1]
            parent_id = get_parent_project(os.path.join(product_name, 'trunk', 'ApkVersion.ini'))
            project_id = add_project(project_name, parent_id, apk_version_path)
            # 解析配置文件
            add_config(project_id, project_config_path)


if __name__ == '__main__':
    main(sys.argv[1])<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""
import os
import sys
from optparse import OptionParser
import commands


def remove_remote(platform_path, remote_name):
    """
    获取平台下所有的Git仓库的指定remote
    """

    repos = []
    for root, dirs, files in os.walk(platform_path):
        for dir_name in dirs:
            if dir_name == '.repo' or dir_name == '.git':
                continue
            git_path = os.path.join(root, dir_name)
            if git_path.find('/.repo/') > 0:
                continue
            if git_path.endswith('.git'):
                print git_path
                status, info = commands.getstatusoutput('git --git-dir={0} remote'.format(git_path))
                if status:
                    print info
                    sys.exit(1)
                elif not info or remote_name not in [tmp for tmp in info.split('\n') if tmp.strip()]:
                    continue
                status, info = commands.getstatusoutput('git --git-dir={0} remote rm {1}'.format(git_path, remote_name))
                if status:
                    print info
                    sys.exit(1)
                print info

    return repos


def main():
    """
    1. 导入服务器上的所有平台
    2. 导入平台下的所有仓库
    """

    des = u'import platform and Repository'
    prog= u'AddRepository'
    ver = u'%prog v0.0.1'
    usage = u'%prog ip_address platform_path dir_path'

    parse = OptionParser(description=des, prog=prog, version=ver, usage=usage, add_help_option=True)

    parse.add_option('-p', '--platform-path', action='store', type='string', dest='platform_path',
                     help='repository platform name')

    parse.add_option('-r', '--remote-name', action='store', type='string', dest='remote_name',
                     help='repository remote name', default='origin')

    # 解析命令行参数
    options, args = parse.parse_args()
    platform_path = options.platform_path.strip('')
    remote_name = options.remote_name

    remove_remote(platform_path, remote_name)

if __name__ == '__main__':
    main()
<file_sep>/**
 * Created by changjie.fan on 2016/8/31.
 */

var common_issue_table = $('#common_issue_table').DataTable({
    "language": {
        "aria": {
            "sortAscending": ": activate to sort column ascending",
            "sortDescending": ": activate to sort column descending"
        },
        "emptyTable": "No data available in table",
        "info": "Showing _START_ to _END_ of _TOTAL_ records",
        "infoEmpty": "No records found",
        "infoFiltered": "(filtered1 from _MAX_ total records)",
        "lengthMenu": "Show _MENU_ records",
        "search": "Search:",
        "zeroRecords": "No matching records found",
        "paginate": {
            "previous": "Prev",
            "next": "Next",
            "last": "Last",
            "first": "First"
        }
    },
    "bFilter": true,
    "bServerSide": true, // save datatable state(pagination, sort, etc) in cookie.
    "bProcessing": true,
    "ajax": {
        url: '/project/common_issue_data',
        data: {"is_all_issue": function(){
            return window.is_all_issue;
        }},
    },
    "columns": [
        {"data": 'index'},
        {"data": 'name'},
        {"data": 'jira_key'},
        {"data": 'project_name'},
        {"data": 'platform_name'},
        {"data": 'status'}
    ],
    // "columnDefs": [
    //  {
    //         "render": function ( data, type, row ) {
    //             return "<a class='btn btn-primary btn-xs' href='"+$SCRIPT_ROOT+"/project/android_project_version/"+ row.id +"'>所有版本</a>";
    //         },
    //         "targets": -1
    //     },
    // ],
    // "createdRow": function ( row, data, index ) {
    //     if(!data['result']){
    //         $(row).css('background-color','red');
    //     }
    // },
    "lengthMenu": [
        [10, 50, 200],
        [10, 50, 200] // change per page values here
    ],
    // set the initial value
    "pageLength": 10,
    "pagingType": "bootstrap_full_number"
});

$('#common_issue_table tbody').on( 'dblclick', 'tr', function () {
    var row_data = common_issue_table.row(this).data().id;
    if (role_name == 'Android测试' || role_name == 'scm'){
        window.location.href = $SCRIPT_ROOT + '/project/qa_edit_common_issue/' + row_data;
    }else if(role_name == 'Android开发工程师') {
        window.location.href = $SCRIPT_ROOT + '/project/dev_edit_common_issue/' + row_data;
    }else if(role_name == 'spm') {
        window.location.href = $SCRIPT_ROOT + '/project/spm_edit_common_issue/' + row_data;
    }
});

//导出Excel数据
function export_issue(){
    $.ajax({
        url: $SCRIPT_ROOT + '/project/export_common_issue',
        success:function(data){
            window.location.href = '/auth/download_file/?file_path=' + JSON.parse(data)["filePath"];
        }
    });
}<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

"""
项目信息管理的view
"""
from datetime import datetime,date
import json
import sys
import os
reload(sys)
sys.setdefaultencoding('utf8')

from flask import render_template, redirect, url_for, request, flash, current_app, session, send_file
from flask.ext.login import login_user, logout_user, login_required, current_user
from flask.ext.mail import Mail, Message
from functools import update_wrapper

from . import project
from tms import db, mail
from tms.auth.models import User, Role
from androidProjectModules import *
from models import Project
from tms.auth.models import SystemParameter
from tms.baseline.models import BPlatform
import MySQLdb
from MySQLdb import connect
from tms.utils.excelOption import ExcelOptions

@project.route('/android_project_page')
@login_required
def android_project_page():
    """
    项目列表页面
    :return:
    """

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '产品项目', 'url': url_for('project.android_project_page')})
    session['menu_path'] = ['项目管理', '产品项目']

    # 关注的项目
    follower_project = ProjectAndroid.query.join(User.project_android_f).filter(User.id == current_user.id).all()
    result = []
    projects = ProjectAndroid.query.order_by(ProjectAndroid.id.desc()).all()

    for project_obj in projects:
        if project_obj in follower_project:
            result.insert(0, (project_obj, True))
        else:
            result.append((project_obj, False))

    return render_template('project/android/project_page.html', projects=result)


@project.route('/add_android_project_page/', methods=['GET', 'POST'])
@login_required
def add_android_project_page():
    """
    新增项目
    :return:
    """

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '新增项目信息' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index + 1]
            break
    else:
        session['breadcrumbs'].append({'text': '新增项目信息', 'url': url_for('project.add_android_project_page')})

    project_obj = ProjectAndroid()

    if request.method == 'POST':
        project_name = request.form.get('name', '').strip()
        project_key = request.form.get('project_key', '').strip()
        category = request.form.get('category', '').strip()
        commit_notice_email = request.form.get('commit_notice_email', '').strip()
        parent_id = request.form.get('parent_id', '')
        android_version = request.form.get('android_version', '')
        status = request.form.get('status', '')
        platform_name = request.form.get('platform_name', '')
        customer_name = request.form.get('customer_name', '')
        customer_area = request.form.get('customer_area', '')
        operators = request.form.get('operators', '')
        extended_field = request.form.get('extended_field', '')
        baseline_name = request.form.get('baseline_name', '')
        scm = request.form.get('scm', '')
        spm = request.form.get('spm', '')
        myos_config_patch = request.form.get('myos_config_patch', '')

        # 项目JIRA_KEY不能重复
        other_project = ProjectAndroid.query.filter(ProjectAndroid.project_key == project_key).first()
        if other_project:
            flash(u'相同JIRA_KEY的项目已存在:%s' % other_project.name, 'warning')
        else:
            project_obj.name = project_name
            project_obj.project_key = project_key
            project_obj.category = category
            project_obj.android_version = android_version
            project_obj.status = status
            project_obj.platform_name = platform_name
            project_obj.customer_name = customer_name
            project_obj.customer_area = customer_area
            project_obj.operators = operators
            project_obj.extended_field = extended_field
            project_obj.baseline_name = baseline_name
            project_obj.create_time = datetime.now()
            project_obj.scm_id = scm
            project_obj.spm_id = spm
            project_obj.myos_config_patch = myos_config_patch

            commit_notice_email_list = list(set([tmp.strip() for tmp in commit_notice_email.split(';') if tmp.strip()]))
            project_obj.commit_notice_email = ';'.join(commit_notice_email_list)
            if parent_id:
                project_obj.parent_id = int(parent_id)

            db.session.add(project_obj)
            db.session.commit()
            flash(u'新增', 'info')
            return redirect(url_for('project.android_project_page'))

    project_category = ['产品项目', '订单项目', '软件项目']
    parent_projects = ProjectAndroid.query.filter(ProjectAndroid.category == '产品项目').all()
    platforms = BPlatform.query.all()

    # 软件项目经理
    spm_options = User.query.join(Role).filter(db.and_(Role.name == 'spm', User.activate == True)).all()
    # 配置管理工程师
    scm_options = User.query.join(Role).filter(db.and_(Role.name == 'scm', User.activate == True)).all()

    # 系统参数值
    # 订单项目客户
    customer_name_value = SystemParameter.query.filter_by(key='customer_name').first()
    if customer_name_value:
        customer_name_options = [tmp.strip() for tmp in customer_name_value.value.split('\n')]
        customer_name_options.sort()
    else:
        customer_name_options = []

    # 订单项目出货地点
    customer_area_value = SystemParameter.query.filter_by(key='customer_area').first()
    if customer_area_value:
        customer_area_options = [tmp.strip() for tmp in customer_area_value.value.split('\n')]
        customer_area_options.sort()
    else:
        customer_area_options = []

    # 运营商参数
    operators_value = SystemParameter.query.filter_by(key='operator').first()
    if operators_value:
        operators_options = [tmp.strip() for tmp in operators_value.value.split('\n')]
        operators_options.sort()
    else:
        operators_options = []
    # android 版本参数
    android_version_value = SystemParameter.query.filter_by(key='android_version').first()
    if android_version_value:
        android_version = [tmp.strip() for tmp in android_version_value.value.split('\n')]
    else:
        android_version = []
    # 项目状态参数
    project_status_value = SystemParameter.query.filter_by(key='project_status').first()
    if project_status_value:
        project_status = [tmp.strip() for tmp in project_status_value.value.split('\n')]
    else:
        project_status = []

    return render_template('project/android/add_project_page.html',
                           project=project_obj,
                           project_category=project_category,
                           parent_projects=parent_projects,
                           platforms=platforms,
                           customer_name_options=customer_name_options,
                           customer_area_options=customer_area_options,
                           operators_options=operators_options,
                           android_version=android_version,
                           project_status=project_status,
                           scm_options=scm_options,
                           spm_options=spm_options)


@project.route('/edit_android_project_page/<int:project_id>', methods=['GET', 'POST'])
@login_required
def edit_android_project_page(project_id):
    """
    修改项目
    :return:
    """

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '编辑项目信息' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index + 1]
            break
    else:
        session['breadcrumbs'].append({'text': '编辑项目信息', 'url': url_for('project.edit_android_project_page',
                                                                        project_id=project_id)})

    project_obj = ProjectAndroid.query.filter(ProjectAndroid.id == project_id).first()
    project_info_obj = ProjectAndroidInfo.query.filter(ProjectAndroidInfo.project_android_id == project_obj.id).first()
    project_history_list = get_project_history(project_id)
    if request.method == 'POST':
        project_name = request.form.get('name', '').strip()
        project_key = request.form.get('project_key', '').strip()
        category = request.form.get('category', '').strip()
        commit_notice_email = request.form.get('commit_notice_email', '').strip()
        parent_id = request.form.get('parent_id', '')
        android_version = request.form.get('android_version', '')
        status = request.form.get('status', '')
        platform_name = request.form.get('platform_name', '')
        customer_name = request.form.get('customer_name', '')
        customer_area = request.form.get('customer_area', '')
        operators = request.form.get('operators', '')
        extended_field = request.form.get('extended_field', '')
        baseline_name = request.form.get('baseline_name', '')
        scm_id = request.form.get('scm', '')
        myos_config_patch = request.form.get('myos_config_patch', '')
        spm_id = request.form.get('spm', '')
        parent_id = request.form.get('parent_id', '')

        # 项目JIRA_KEY不能重复
        other_project = ProjectAndroid.query.filter(ProjectAndroid.project_key == project_key).first()
        if other_project and other_project.id != project_obj.id:
            flash(u'相同JIRA_KEY的项目已存在:%s' % other_project.name, 'warning')
        else:
            # 记录修改历史到project_android_history
            history = {}
            history['time'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            history['user_id'] = current_user.id
            history['project_id'] = project_id
            history['type'] = 'edit'
            history['content'] = []

            if project_obj.name != project_name:
                case = {}
                case['name'] = '项目名称'
                case['old'] = project_obj.name
                case['new'] = project_name
                history['content'].append(case)
                project_obj.name = project_name

            if project_obj.project_key != project_key:
                case = {}
                case['name'] = '项目JIRA_KEY'
                case['old'] = project_obj.project_key
                case['new'] = project_key
                history['content'].append(case)
                project_obj.project_key = project_key

            if project_obj.parent_id != parent_id:
                case = {}
                case['name'] = '产品项目'
                case['old'] = project_obj.parent_id
                case['new'] = parent_id
                history['content'].append(case)
                if parent_id:
                    project_obj.parent_id = parent_id
                else:
                    project_obj.parent = None

            if project_obj.category != category:
                case = {}
                case['name'] = '项目类型'
                case['old'] = project_obj.category
                case['new'] = category
                history['content'].append(case)
                project_obj.category = category

            if project_obj.android_version != android_version:
                case = {}
                case['name'] = 'Android版本'
                case['old'] = project_obj.android_version
                case['new'] = android_version
                history['content'].append(case)
                project_obj.android_version = android_version

            if project_obj.status != status:
                case = {}
                case['name'] = '项目状态'
                case['old'] = project_obj.status
                case['new'] = status
                history['content'].append(case)
                project_obj.status = status

            if project_obj.platform_name != platform_name:
                case = {}
                case['name'] = '平台厂商'
                case['old'] = project_obj.platform_name
                case['new'] = platform_name
                history['content'].append(case)
                project_obj.platform_name = platform_name

            if project_obj.customer_name != customer_name:
                case = {}
                case['name'] = '订单项目客户名称'
                case['old'] = project_obj.customer_name
                case['new'] = customer_name
                history['content'].append(case)
                project_obj.customer_name = customer_name

            if project_obj.customer_area != customer_area:
                case = {}
                case['name'] = '订单项目出货地点'
                case['old'] = project_obj.customer_area
                case['new'] = customer_area
                history['content'].append(case)
                project_obj.customer_area = customer_area

            if project_obj.operators != operators:
                case = {}
                case['name'] = '运营商名称'
                case['old'] = project_obj.operators
                case['new'] = operators
                history['content'].append(case)
                project_obj.operators = operators

            if project_obj.extended_field != extended_field:
                case = {}
                case['name'] = '扩展字段'
                case['old'] = project_obj.extended_field
                case['new'] = extended_field
                history['content'].append(case)
                project_obj.extended_field = extended_field

            if project_obj.baseline_name != baseline_name:
                case = {}
                case['name'] = '基线名称'
                case['old'] = project_obj.baseline_name
                case['new'] = baseline_name
                history['content'].append(case)
                project_obj.baseline_name = baseline_name

            if project_obj.myos_config_patch != myos_config_patch:
                case = {}
                case['name'] = 'MyOS配置路径'
                case['old'] = project_obj.myos_config_patch
                case['new'] = myos_config_patch
                history['content'].append(case)
                project_obj.myos_config_patch = myos_config_patch

            if project_obj.scm_id != int(scm_id):
                scm = User.query.filter_by(id=scm_id).first()
                case = {}
                case['name'] = '配置管理工程师'
                if project_obj.scm:
                    case['old'] = project_obj.scm.display_name
                else:
                    case['old'] = ''
                case['new'] = scm.display_name
                history['content'].append(case)
                project_obj.scm = scm

            if project_obj.spm_id != int(spm_id):
                spm = User.query.filter_by(id=spm_id).first()
                case = {}
                case['name'] = '软件项目经理'
                if project_obj.spm:
                    case['old'] = project_obj.spm.display_name
                else:
                    case['old'] = ''
                case['new'] = spm.display_name
                history['content'].append(case)
                project_obj.spm = spm

            # 去除重复的邮箱地址
            if commit_notice_email:
                commit_notice_email_list = list(
                    set([tmp.strip() for tmp in commit_notice_email.split(';') if tmp.strip()]))
                old_email_list = []
                if project_obj.commit_notice_email:
                    old_email_list = project_obj.commit_notice_email.split(';')
                if project_obj.commit_notice_email != ';'.join(commit_notice_email_list):
                    project_obj.commit_notice_email = ';'.join(commit_notice_email_list)
                    # 项目中删除的邮箱在分支中也要删除
                    need_delete_email = []
                    for old_email in old_email_list:
                        if old_email not in commit_notice_email_list:
                            need_delete_email.append(old_email)

                    # 更新所有分支的通知邮箱地址
                    project_code_list = project_obj.project_code
                    for project_code in project_code_list:
                        if project_code.commit_notice_email:
                            new_project_code_email = []
                            for email in project_code.commit_notice_email.split(';'):
                                if email not in need_delete_email:
                                    new_project_code_email.append(email)
                            project_code.commit_notice_email = ';'.join(
                                list(set(new_project_code_email + commit_notice_email_list)))
                        else:
                            project_code.commit_notice_email = project_obj.commit_notice_email
                        db.session.add(project_code)

            if parent_id and project_obj.parent_id != project_id:
                if project_obj.parent_id != int(parent_id):
                    case = {}
                    case['name'] = '基础项目'
                    case['old'] = project_obj.parent_id
                    case['new'] = parent_id
                    history['content'].append(case)
                project_obj.parent_id = int(parent_id)
            add_project_history(history)
            db.session.add(project_obj)
            db.session.commit()

            flash(u'修改成功', 'info')
            return redirect(url_for('project.android_project_page'))

    project_code = project_obj.project_code

    project_category = ['产品项目', '订单项目', '软件项目']
    parent_projects = ProjectAndroid.query.filter(ProjectAndroid.category == '产品项目').all()
    platforms = BPlatform.query.all()

    # 软件项目经理
    spm_options = User.query.join(Role).filter(db.and_(Role.name == 'spm', User.activate == True)).all()
    # 配置管理工程师
    scm_options = User.query.join(Role).filter(db.and_(Role.name == 'scm', User.activate == True)).all()

    # 系统参数
    # 订单项目客户
    customer_name_value = SystemParameter.query.filter_by(key='customer_name').first()
    if customer_name_value:
        customer_name_options = [tmp.strip() for tmp in customer_name_value.value.split('\n')]
        customer_name_options.sort()
    else:
        customer_name_options = []

    # 订单项目出货地点
    customer_area_value = SystemParameter.query.filter_by(key='customer_area').first()
    if customer_area_value:
        customer_area_options = [tmp.strip() for tmp in customer_area_value.value.split('\n')]
        customer_area_options.sort()
    else:
        customer_area_options = []

    # 运营商参数
    operators_value = SystemParameter.query.filter_by(key='operator').first()
    if operators_value:
        operators_options = [tmp.strip() for tmp in operators_value.value.split('\n')]
        operators_options.sort()
    else:
        operators_options = []
    # android 版本参数
    android_version_value = SystemParameter.query.filter_by(key='android_version').first()
    if android_version_value:
        android_version = [tmp.strip() for tmp in android_version_value.value.split('\n')]
    else:
        android_version = []
    # 项目状态参数
    project_status_value = SystemParameter.query.filter_by(key='project_status').first()
    if project_status_value:
        project_status = [tmp.strip() for tmp in project_status_value.value.split('\n')]
    else:
        project_status = []

    return render_template('project/android/edit_project_page.html',
                           project=project_obj,
                           project_info=project_info_obj,
                           project_history_list=project_history_list,
                           project_code=project_code,
                           project_category=project_category,
                           parent_projects=parent_projects,
                           platforms=platforms,
                           customer_name_options=customer_name_options,
                           customer_area_options=customer_area_options,
                           operators_options=operators_options,
                           android_version=android_version,
                           project_status=project_status,
                           scm_options=scm_options,
                           spm_options=spm_options)


@project.route('/android_project_jira')
@login_required
def android_project_jira():
    # 项目Jira统计

    jira_key = request.args.get('jira_key', '')
    date_vrgs = request.args.get('date', '')
    export_flag = request.args.get('export','')
    # if not date_vrgs:
    #     date_vrgs = str(date.today())
    jira_data = {}
    # 任务每日更新,包括:按日查询新增任务,已关闭任务
    tasks_daily_update_sql = "SELECT open_issue.Jdate,open_issue.openNum,close_issue.closedNum FROM " \
                             "(SELECT DATE(jiraissue.CREATED) as Jdate,COUNT(DATE(jiraissue.CREATED)) as openNum FROM jiraissue LEFT JOIN project ON project.ID = jiraissue.PROJECT " \
                             "WHERE project.pkey = '%s' AND jiraissue.issuetype = 1 GROUP BY DATE(jiraissue.CREATED)) open_issue " \
                             "LEFT JOIN " \
                             "(SELECT DATE(jiraissue.UPDATED) as Jdate,COUNT(DATE(jiraissue.UPDATED)) as closedNum FROM jiraissue LEFT JOIN project ON project.ID = jiraissue.PROJECT " \
                             "WHERE project.pkey = '%s' AND jiraissue.issuestatus = 6 AND jiraissue.issuetype = 1 GROUP BY DATE(jiraissue.UPDATED)) close_issue ON open_issue.Jdate = close_issue.Jdate " \
                             "UNION " \
                             "SELECT close_issue.Jdate,open_issue.openNum,close_issue.closedNum FROM " \
                             "(SELECT DATE(jiraissue.CREATED) as Jdate,COUNT(DATE(jiraissue.CREATED)) as openNum FROM jiraissue LEFT JOIN project ON project.ID = jiraissue.PROJECT " \
                             "WHERE project.pkey = '%s' AND jiraissue.issuetype = 1 GROUP BY DATE(jiraissue.CREATED)) open_issue " \
                             "RIGHT JOIN " \
                             "(SELECT DATE(jiraissue.UPDATED) as Jdate,COUNT(DATE(jiraissue.UPDATED)) as closedNum FROM jiraissue LEFT JOIN project ON project.ID = jiraissue.PROJECT " \
                             "WHERE project.pkey = '%s' AND jiraissue.issuestatus = 6 AND jiraissue.issuetype = 1 GROUP BY DATE(jiraissue.UPDATED)) close_issue ON open_issue.Jdate = close_issue.Jdate " \
                             "ORDER BY Jdate " % (jira_key, jira_key, jira_key, jira_key)
    # # 每人负责的任务总数
    # total_tasks_each_person_sql = "SELECT cu.display_name as assigner,COUNT(jiraissue.ASSIGNEE) as assignerNum FROM jiraissue LEFT JOIN project ON project.ID = jiraissue.PROJECT " \
    #                               "LEFT JOIN cwd_user cu ON cu.user_name = jiraissue.ASSIGNEE OR cu.email_address = jiraissue.ASSIGNEE " \
    #                               "WHERE project.pkey = '%s' GROUP BY jiraissue.ASSIGNEE ORDER BY COUNT(jiraissue.ASSIGNEE) DESC LIMIT 0,60 " % jira_key
    # # 每人负责的正在进行中的任务数
    total_tasks_developer_open_sql = "SELECT cu.display_name as assigner,COUNT(jiraissue.ASSIGNEE) as assignerNum FROM jiraissue LEFT JOIN project ON project.ID = jiraissue.PROJECT " \
                                       "LEFT JOIN cwd_user cu ON cu.user_name = jiraissue.ASSIGNEE OR cu.email_address = jiraissue.ASSIGNEE " \
                                       "WHERE project.pkey = '%s' AND jiraissue.issuestatus != 6 AND jiraissue.issuetype = 1 GROUP BY jiraissue.ASSIGNEE ORDER BY COUNT(jiraissue.ASSIGNEE) DESC LIMIT 0,60" % jira_key
    # 全部未关闭的任务数
    total_tasks_open_sql = "SELECT count(*) AS openNum FROM jiraissue LEFT JOIN project ON project.ID = jiraissue.PROJECT WHERE project.pkey = '%s' AND jiraissue.issuestatus != 6 AND jiraissue.issuetype = 1 " % jira_key
    # 全部已关闭的任务数
    total_tasks_closed_sql = "SELECT count(*) AS closeNum FROM jiraissue LEFT JOIN project ON project.ID = jiraissue.PROJECT WHERE project.pkey = '%s' AND jiraissue.issuestatus = 6 AND jiraissue.issuetype = 1 " % jira_key

    #测试人员每个人提交的总任务数
    total_tasks_tester_open_sql = "SELECT cu.display_name as creator,COUNT(*) as creatorNum FROM jiraissue LEFT JOIN project ON project.ID = jiraissue.PROJECT " \
                                      "LEFT JOIN cwd_user cu ON cu.user_name = jiraissue.CREATOR OR cu.email_address = jiraissue.CREATOR " \
                                      "WHERE project.pkey = '%s' AND jiraissue.issuetype = 1 GROUP BY jiraissue.CREATOR ORDER BY creatorNum desc" % jira_key
    #测试人员当日每个人提交的任务数
    daily_tasks_tester_open_sql = "SELECT cu.display_name as creator,COUNT(*) as creatorNum FROM jiraissue LEFT JOIN project ON project.ID = jiraissue.PROJECT " \
                                      "LEFT JOIN cwd_user cu ON cu.user_name = jiraissue.CREATOR OR cu.email_address = jiraissue.CREATOR " \
                                      "WHERE project.pkey = '%s' AND DATE(jiraissue.CREATED) = '%s' AND jiraissue.issuetype = 1 GROUP BY jiraissue.CREATOR ORDER BY creatorNum desc" % (jira_key,date.today() if not date_vrgs else date_vrgs)
    #开发人员每个人关闭的总任务数
    total_tasks_developer_closed_sql = "SELECT cu.display_name as assigner,COUNT(*) as assignerNum FROM jiraissue LEFT JOIN project ON project.ID = jiraissue.PROJECT " \
                                        "LEFT JOIN cwd_user cu ON cu.user_name = jiraissue.ASSIGNEE OR cu.email_address = jiraissue.ASSIGNEE " \
                                        "WHERE project.pkey = '%s' AND jiraissue.issuestatus = 6 AND jiraissue.issuetype = 1 GROUP BY jiraissue.ASSIGNEE ORDER BY assignerNum desc" % jira_key
    #开发人员当日每个人关闭的任务数
    daily_tasks_developer_closed_sql = "SELECT cu.display_name as assigner,COUNT(*) as assignerNum FROM jiraissue LEFT JOIN project ON project.ID = jiraissue.PROJECT " \
                                        "LEFT JOIN cwd_user cu ON cu.user_name = jiraissue.ASSIGNEE OR cu.email_address = jiraissue.ASSIGNEE " \
                                        "WHERE project.pkey = '%s' AND jiraissue.issuestatus = 6 AND DATE(jiraissue.UPDATED) = '%s' AND jiraissue.issuetype = 1 GROUP BY jiraissue.ASSIGNEE ORDER BY assignerNum desc" % (jira_key,date.today() if not date_vrgs else date_vrgs)
    #按模块统计任务数，（存在同一个issue属于两个己以上模块）
    total_tasks_by_module_sql = "SELECT total_talbe.module_name,total_talbe.total_num,open_table.open_num FROM " \
                                    "(SELECT component.cname AS module_name,COUNT(*) AS total_num FROM jiraissue " \
                                        "LEFT JOIN project ON project.ID = jiraissue.PROJECT " \
                                        "LEFT JOIN nodeassociation ON jiraissue.ID = nodeassociation.SOURCE_NODE_ID " \
                                        "LEFT JOIN component ON nodeassociation.SINK_NODE_ID = component.ID " \
                                    "WHERE project.pkey = '%s' AND nodeassociation.ASSOCIATION_TYPE = 'IssueComponent' AND jiraissue.issuetype = 1 " \
                                    "GROUP BY component.cname) total_talbe " \
                                "LEFT JOIN " \
                                    "(SELECT component.cname AS module_name,COUNT(*) AS open_num FROM jiraissue " \
                                        "LEFT JOIN project ON project.ID = jiraissue.PROJECT " \
                                        "LEFT JOIN nodeassociation ON jiraissue.ID = nodeassociation.SOURCE_NODE_ID " \
                                        "LEFT JOIN component ON nodeassociation.SINK_NODE_ID = component.ID " \
                                    "WHERE project.pkey = '%s' AND nodeassociation.ASSOCIATION_TYPE = 'IssueComponent' AND jiraissue.issuetype = 1 AND jiraissue.issuestatus != 6 " \
                                    "GROUP BY component.cname) open_table " \
                                "ON total_talbe.module_name = open_table.module_name ORDER BY total_talbe.total_num desc" % (jira_key,jira_key)

    jira_db_host = current_app.config['JIRA_DB_HOST']
    jira_db_name = current_app.config['JIRA_DB_NAME']
    jira_db_user = current_app.config['JIRA_DB_USER']
    jira_db_passwd = current_app.config['JIRA_DB_PASSWD']

    conn = connect(host=jira_db_host, db=jira_db_name, user=jira_db_user, passwd=<PASSWORD>, charset='utf8')
    cursor = conn.cursor()

    cursor.execute(tasks_daily_update_sql)
    tasks_daily_update_records = cursor.fetchall()

    # cursor.execute(total_tasks_each_person_sql)
    # total_tasks_each_person_records = cursor.fetchall()
    #
    cursor.execute(total_tasks_developer_open_sql)
    total_tasks_developer_open_records = cursor.fetchall()

    cursor.execute(total_tasks_open_sql)
    total_tasks_open_records = cursor.fetchall()

    cursor.execute(total_tasks_closed_sql)
    total_tasks_closed_records = cursor.fetchall()

    cursor.execute(total_tasks_tester_open_sql)
    total_tasks_tester_open_records = cursor.fetchall()

    cursor.execute(daily_tasks_tester_open_sql)
    daily_tasks_tester_open_records = cursor.fetchall()

    cursor.execute(total_tasks_developer_closed_sql)
    total_tasks_developer_closed_records = cursor.fetchall()

    cursor.execute(daily_tasks_developer_closed_sql)
    daily_tasks_developer_closed_records = cursor.fetchall()

    cursor.execute(total_tasks_by_module_sql)
    total_tasks_by_module_records = cursor.fetchall()

    cursor.close()
    conn.close()

    if not date_vrgs:
        total_tasks = []
        for total_tasks_open_record in total_tasks_open_records:
            total_tasks_open_dic = {}
            total_tasks_open_dic["status"] = "进行中"
            total_tasks_open_dic["total_num"] = total_tasks_open_record[0]
            total_tasks.append(total_tasks_open_dic)

        for total_tasks_closed_record in total_tasks_closed_records:
            total_tasks_closed_dic = {}
            total_tasks_closed_dic["status"] = "已完成"
            total_tasks_closed_dic["total_num"] = total_tasks_closed_record[0]
            total_tasks.append(total_tasks_closed_dic)
        jira_data['total_tasks'] = total_tasks

        # tasks_daily_update = []
        # for tasks_daily_update_record in tasks_daily_update_records:
        #     tasks_daily_update_dic = {}
        #     tasks_daily_update_dic["date"] = tasks_daily_update_record[0].strftime("%Y-%m-%d")
        #     tasks_daily_update_dic["new_open"] = 0 if tasks_daily_update_record[1] is None else int(
        #         tasks_daily_update_record[1])
        #     tasks_daily_update_dic["closed"] = 0 if tasks_daily_update_record[2] is None else int(
        #         tasks_daily_update_record[2])
        #     tasks_daily_update.append(tasks_daily_update_dic)
        # jira_data['tasks_daily_update'] = tasks_daily_update

        tasks_daily_summary = []
        for index1,tasks_daily_update_record in enumerate(tasks_daily_update_records):
            tasks_daily_summary_dic = {}
            open_summary = 0
            closed_summary = 0
            tasks_daily_summary_dic["date"] = tasks_daily_update_record[0].strftime("%Y-%m-%d")
            for index2,tasks_daily_update_sub in enumerate(tasks_daily_update_records):
                if index1 >= index2:
                    open_summary += 0 if tasks_daily_update_sub[1] is None else int(tasks_daily_update_sub[1])
                    closed_summary += 0 if tasks_daily_update_sub[2] is None else int(tasks_daily_update_sub[2])
                else:
                    break
            tasks_daily_summary_dic["open_summary"] = open_summary
            tasks_daily_summary_dic["closed_summary"] = closed_summary

            tasks_daily_summary.append(tasks_daily_summary_dic)
        jira_data['tasks_daily_summary'] = tasks_daily_summary


        # total_tasks_each_person = []
        # for total_tasks_each_person_record in total_tasks_each_person_records:
        #     total_tasks_each_person_dic = {}
        #     total_tasks_each_person_dic["assigner"] = total_tasks_each_person_record[0]
        #     total_tasks_each_person_dic["total_tasks"] = total_tasks_each_person_record[1]
        #     total_tasks_each_person.append(total_tasks_each_person_dic)
        # jira_data['total_tasks_each_person'] = total_tasks_each_person
        #
        total_tasks_developer_open = []
        for total_tasks_developer_open_record in total_tasks_developer_open_records:
            total_tasks_developer_open_dic = {}
            total_tasks_developer_open_dic["assigner"] = total_tasks_developer_open_record[0]
            total_tasks_developer_open_dic["total_tasks"] = total_tasks_developer_open_record[1]
            total_tasks_developer_open.append(total_tasks_developer_open_dic)
        jira_data['total_tasks_developer_open'] = total_tasks_developer_open

        total_tasks_by_module = []
        for total_tasks_by_module_record in total_tasks_by_module_records:
            total_tasks_by_module_dic = {}
            total_tasks_by_module_dic['module_name'] = total_tasks_by_module_record[0]
            total_tasks_by_module_dic['total_num'] = 0 if total_tasks_by_module_record[1] is None else int(total_tasks_by_module_record[1])
            total_tasks_by_module_dic['open_num'] = 0 if total_tasks_by_module_record[2] is None else int(total_tasks_by_module_record[2])
            total_tasks_by_module.append(total_tasks_by_module_dic)
        jira_data['total_tasks_by_module'] = total_tasks_by_module

    total_tasks_tester_open = []
    for total_tasks_tester_open_record in total_tasks_tester_open_records:
        total_tasks_tester_open_dic = {}
        total_tasks_tester_open_dic['creator'] = total_tasks_tester_open_record[0]
        total_tasks_tester_open_dic['creatorNum'] = total_tasks_tester_open_record[1]
        total_tasks_tester_open.append(total_tasks_tester_open_dic)
    jira_data['total_tasks_tester_open'] = total_tasks_tester_open


    daily_tasks_tester_open = []
    for daily_tasks_tester_open_record in daily_tasks_tester_open_records:
        daily_tasks_tester_open_dic = {}
        daily_tasks_tester_open_dic['creator'] = daily_tasks_tester_open_record[0]
        daily_tasks_tester_open_dic['creatorNum'] = daily_tasks_tester_open_record[1]
        daily_tasks_tester_open.append(daily_tasks_tester_open_dic)
    jira_data['daily_tasks_tester_open'] = daily_tasks_tester_open

    total_tasks_developer_closed = []
    for total_tasks_developer_closed_record in total_tasks_developer_closed_records:
        total_tasks_developer_closed_dic = {}
        total_tasks_developer_closed_dic['assigner'] = total_tasks_developer_closed_record[0]
        total_tasks_developer_closed_dic['assignerNum'] = total_tasks_developer_closed_record[1]
        total_tasks_developer_closed.append(total_tasks_developer_closed_dic)
    jira_data['total_tasks_developer_closed'] = total_tasks_developer_closed


    daily_tasks_developer_closed = []
    for daily_tasks_developer_closed_record in daily_tasks_developer_closed_records:
        daily_tasks_developer_closed_dic = {}
        daily_tasks_developer_closed_dic['assigner'] = daily_tasks_developer_closed_record[0]
        daily_tasks_developer_closed_dic['assignerNum'] = daily_tasks_developer_closed_record[1]
        daily_tasks_developer_closed.append(daily_tasks_developer_closed_dic)
    jira_data['daily_tasks_developer_closed'] = daily_tasks_developer_closed

    jira_data = json.dumps(jira_data)

    if export_flag:
        project_name = ProjectAndroid.query.filter(ProjectAndroid.project_key==jira_key).first().name
        excel_name = project_name + '_jira_' + str(date.today())
        excel_option = ExcelOptions(os.path.join(current_app.config['TMS_TEMP_DIR'], "jira/%s.xls" % excel_name))
        excel_option.export_jira_xls(jira_data)
        return excel_option.file_path

    return jira_data


def add_project_history(history):
    """添加项目修改操作历史记录"""

    project_history_obj = ProjectAndroidHistory()
    project_history_obj.project_android_id = history['project_id']
    project_history_obj.operate_time = history['time']
    project_history_obj.operate_user_id = history['user_id']
    project_history_obj.operate_type = history['type']
    content = {}
    content['content'] = history['content']
    project_history_obj.operate_content = json.dumps(content, ensure_ascii=False)
    db.session.add(project_history_obj)
    db.session.commit()


def get_project_history(project_id):
    """
    获取每个项目的修改历史记录，只有scm有权限查看
    :param project_id:
    :return:
    """
    project_history_list_1 = ProjectAndroidHistory.query.filter(
        ProjectAndroidHistory.project_android_id == project_id).order_by(
        ProjectAndroidHistory.operate_time.desc()).all()
    project_history_list = []

    for project_history in project_history_list_1:
        history = {}
        history['id'] = project_history.id
        history['project_android_id'] = project_history.project_android_id
        history['operate_time'] = project_history.operate_time
        history['operate_user'] = project_history.operate_user.display_name
        history['operate_type'] = project_history.operate_type
        content_obj = json.loads(project_history.operate_content)
        history['operate_content'] = content_obj['content']
        project_history_list.append(history)
    return project_history_list


@project.route('/edit_android_project_info/<int:project_id>', methods=['GET', 'POST'])
@login_required
def edit_android_project_info(project_id):
    """
    修改项目关联信息
    :return:
    """
    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '编辑项目信息' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index + 1]
            break
    else:
        session['breadcrumbs'].append({'text': '编辑项目信息', 'url': url_for('project.edit_android_project_page',
                                                                        project_id=project_id)})

    project_info_obj = ProjectAndroidInfo.query.filter(ProjectAndroidInfo.project_android_id == project_id).first()
    operate_type = 'edit'
    if not project_info_obj:
        project_info_obj = ProjectAndroidInfo()
        project_info_obj.project_android_id = project_id
        operate_type = 'add'
    if request.method == 'POST':
        jenkins_url = request.form.get('jenkins_url', '').strip()
        coverity_url = request.form.get('coverity_url', '').strip()
        ftp_url = request.form.get('ftp_url', '').strip()
        svn_url = request.form.get('svn_url', '').strip()
        gerrit_url = request.form.get('gerrit_url', '').strip()
        push_command = request.form.get('push_command', '').strip()
        other_command = request.form.get('other_command', '').strip()
        parent_id = request.form.get('parent_id', '')

        # 记录修改历史到project_android_history
        history = {}
        history['time'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        history['user_id'] = current_user.id
        history['project_id'] = project_id
        history['type'] = operate_type
        history['content'] = []
        if project_info_obj.jenkins_url != jenkins_url:
            case = {}
            case['name'] = 'jenkins_url'
            case['old'] = project_info_obj.jenkins_url
            case['new'] = jenkins_url
            history['content'].append(case)
        if project_info_obj.coverity_url != coverity_url:
            case = {}
            case['name'] = 'coverity_url'
            case['old'] = project_info_obj.coverity_url
            case['new'] = coverity_url
            history['content'].append(case)
        if project_info_obj.ftp_url != ftp_url:
            case = {}
            case['name'] = 'ftp_url'
            case['old'] = project_info_obj.ftp_url
            case['new'] = ftp_url
            history['content'].append(case)
        if project_info_obj.svn_url != svn_url:
            case = {}
            case['name'] = 'svn_url'
            case['old'] = project_info_obj.svn_url
            case['new'] = svn_url
            history['content'].append(case)
        if project_info_obj.gerrit_url != gerrit_url:
            case = {}
            case['name'] = 'gerrit_url'
            case['old'] = project_info_obj.gerrit_url
            case['new'] = gerrit_url
            history['content'].append(case)
        if project_info_obj.push_command != push_command:
            case = {}
            case['name'] = 'push_command'
            case['old'] = project_info_obj.push_command
            case['new'] = push_command
            history['content'].append(case)
        if project_info_obj.other_command != other_command:
            case = {}
            case['name'] = 'other_command'
            case['old'] = project_info_obj.other_command
            case['new'] = other_command
            history['content'].append(case)
        if project_info_obj.project_android.parent_id != parent_id:
            case = {}
            case['name'] = 'parent project'
            case['old'] = project_info_obj.project_android.parent_id
            case['new'] = parent_id
            history['content'].append(case)

        # 记录修改历史到project_android_history 结束
        project_info_obj.jenkins_url = jenkins_url
        project_info_obj.coverity_url = coverity_url
        project_info_obj.ftp_url = ftp_url
        project_info_obj.svn_url = svn_url
        project_info_obj.gerrit_url = gerrit_url
        project_info_obj.push_command = push_command
        project_info_obj.other_command = other_command

    db.session.add(project_info_obj)
    db.session.commit()
    add_project_history(history)
    flash(u'修改成功', 'info')
    return redirect(url_for('project.android_project_page'))


@project.route('/start_project')
@login_required
def start_project():
    """关注项目"""

    project_id = request.args.get('project_id', '')
    start_option = request.args.get('start_option', '')
    project_obj = ProjectAndroid.query.filter_by(id=project_id).first()
    if start_option == '1':
        project_obj.followers.append(current_user)
    elif start_option == '-1':
        project_obj.followers.remove(current_user)

    db.session.add(project_obj)
    db.session.commit()

    return '{"result":true}'


@project.route('/spm_my_project')
@login_required
def spm_my_project():
    """spm 我负责的、参与、关注的项目"""

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '我的项目', 'url': url_for('project.spm_my_project')})
    session['menu_path'] = ['项目管理', '我的项目']

    # 关注的项目
    follower_project = ProjectAndroid.query.join(User.project_android_f).filter(User.id == current_user.id).all()
    result = []
    projects = ProjectAndroid.query.order_by(ProjectAndroid.id.desc()).all()

    for project_obj in projects:
        if project_obj in follower_project:
            result.insert(0, (project_obj, True))
        else:
            result.append((project_obj, False))

    return render_template('project/android/project_page.html', projects=result)
<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""
import json
from datetime import datetime

from flask import render_template, redirect, url_for, request, flash, current_app, session
from flask.ext.login import login_required, current_user

from . import qa
from models import *
from tms.qa.views import need_test_version


@qa.route('/test_case_page')
@login_required
@need_test_version()
def test_case_page():
    """用例页面"""
    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '我的用例', 'url': url_for('qa.test_case_page')})
    session['menu_path'] = ['用例管理', '我的用例']

    return render_template('qa/test_case_page.html')


@qa.route('/test_case_data')
@login_required
def test_case_data():
    """用例页面表格数据"""

    start = int(request.args.get('start', 0))
    length = int(request.args.get('length', 10))
    is_all = request.args.get('is_all', 'false')

    if is_all:
        all_count = TestCase.query.filter(TestCase.activate == True).count()
        test_case = TestCase.query.filter(TestCase.activate == True).all()[start: start+length]
    else:
        # 默认现在用例负责人是我的用例
        all_count = TestCase.query.filter(
            db.and_(TestCase.activate == True, TestCase.add_user_id == current_user.id)).count()
        test_case = TestCase.query.filter(
            db.and_(TestCase.activate == True,
                    TestCase.add_user_id == current_user.id)).all()[start: start+length]

    records = []
    index = start + 1
    for record in test_case:
        records.append({
            "id": record.id,
            "index": index,
            "number": record.number,
            "level": record.level,
            "test_type": record.test_type,
            "module_name": record.module_name,
            "test_fun": record.test_fun,
            "class_name": record.class_name,
            "method_name": record.method_name,
            "test_target": record.test_target,
            "standard_sdk": record.standard_sdk,
            "test_init": record.test_init,
            "test_step": record.test_step,
            "expect_result": record.expect_result,
            "activate": record.activate,
            "add_user": record.add_user.display_name,
            "add_time": str(record.add_time),
            "last_user": record.last_user.display_name,
            "last_update_time": str(record.last_update_time),
        })
        index += 1

    return '{"recordsTotal": %s ,"recordsFiltered": %s,"data":%s}' % (all_count, all_count, json.dumps(records))


@qa.route('/edit_test_case', defaults={'id': None}, methods=['GET', 'POST'])
@qa.route('/edit_test_case/<int:id>', methods=['GET', 'POST'])
@login_required
@need_test_version()
def edit_test_case(id):
    """添加或修改测试用例"""

    if id:
        test_case_obj = TestCase.query.filter(TestCase.id == id).first()
    else:
        test_case_obj = None

    if request.method == 'POST':
        number = request.form.get('number', '').strip()
        other_test_case = TestCase.query.filter(TestCase.number == number).first()
        if other_test_case and other_test_case.id != id:
            flash(u'相同编号的测试用例已存在', 'dange')
        else:
            if id:
                message = u'修改成功'
            else:
                test_case_obj = TestCase()
                message = u'添加成功!'

            test_case_history = TestCaseLog()
            test_case_obj.number = number
            test_case_obj.level = request.form.get('level', '').strip()
            test_case_obj.test_type = request.form.get('test_type', '').strip()
            test_case_obj.module_name = request.form.get('module_name', '').strip()
            test_case_obj.class_name = request.form.get('class_name', '').strip()
            test_case_obj.method_name = request.form.get('method_name', '').strip()
            test_case_obj.test_fun = request.form.get('test_fun', '').strip()
            test_case_obj.test_target = request.form.get('test_target', '').strip()
            if request.form.get('standard_sdk', '') == 'on':
                test_case_obj.standard_sdk = True
            else:
                test_case_obj.standard_sdk = False
            test_case_obj.test_init = request.form.get('test_init', '').strip()
            test_case_obj.test_step = request.form.get('test_step', '').strip()
            test_case_obj.expect_result = request.form.get('expect_result', '').strip()
            if request.form.get('activate', '') == 'on':
                test_case_obj.activate = True
            else:
                test_case_obj.activate = False

            if not id:
                test_case_obj.add_user_id = current_user.id
                test_case_obj.add_time = datetime.now()
                test_case_history.content = u'新增测试用例,编号:{0},模块:{1},类名:{2},方法名:{3}' \
                                            u''.format(test_case_obj.number, test_case_obj.module_name,
                                                       test_case_obj.class_name, test_case_obj.method_name)
            else:
                test_case_history.content = u'修改测试用例,编号:{0},模块:{1},类名:{2},方法名:{3}' \
                                            u''.format(test_case_obj.number, test_case_obj.module_name,
                                                       test_case_obj.class_name, test_case_obj.method_name)

            test_case_obj.last_user_id = current_user.id
            test_case_obj.last_update_time = datetime.now()

            db.session.add(test_case_obj)
            db.session.commit()
            test_case_history.test_case = test_case_obj
            test_case_history.user_id = current_user.id
            test_case_history.update_time = datetime.now()
            db.session.add(test_case_history)
            db.session.commit()

            flash(message, 'info')
            return redirect(url_for('qa.test_case_page'))

    if session['user']['role_name'] == 'os_test':
        permission = True
    else:
        permission = False
    case_level = ['S', 'A', 'B', 'C']
    test_type = [u'功能测试', u'冒烟测试']
    return render_template('qa/edit_test_case.html', test_case=test_case_obj, permission=permission,
                           case_level=case_level, test_type=test_type)



<file_sep>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Created by changjie.fan on 2016/8/25
"""
"""
import os
from datetime import datetime
import simplejson as json
from flask import render_template, redirect, url_for, request, flash, current_app, jsonify, g, session, send_file
from flask.ext.login import login_user, logout_user, login_required, current_user

from models import User, Role, SystemMenu, SystemParameter
from . import auth
from tms import db


@auth.route('/system_parameter_page')
@login_required
def system_parameter_page():
    """系统参数页面"""

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': u'系统参数', 'url': url_for('auth.system_parameter_page')})
    session['menu_path'] = [u'系统管理', u'系统参数']

    parameters = SystemParameter.query.all()

    return render_template('admin/system/system_parameter_page.html', parameters=parameters)


@auth.route('/add_system_parameter', methods=['GET', 'POST'])
@login_required
def add_system_parameter():
    """新增系统参数"""

    parameter = SystemParameter()

    if request.method == 'POST':
        key = request.form.get('key', '').strip()
        value = request.form.get('value', '').strip().replace('\r', '\n')
        flag = request.form.get('flag', 'off')
        note = request.form.get('note', '').strip()

        other = SystemParameter.query.filter(SystemParameter.key == key).first()
        if other:
            flash('相同的系统参数已存在', 'warning')
        else:
            parameter.key = key
            parameter.value = '\n'.join(value.split('\n'))
            if flag == 'on':
                parameter.flag = True
            else:
                parameter.flag = False
            parameter.note = note

            db.session.add(parameter)
            db.session.commit()
            return  redirect(url_for('auth.system_parameter_page'))

    return render_template('admin/system/system_parameter_edit.html', parameter=parameter)


@auth.route('/edit_system_parameter/<int:parameter_id>', methods=['GET', 'POST'])
@login_required
def edit_system_parameter(parameter_id):
    """修改系统参数"""

    parameter = SystemParameter.query.filter_by(id=parameter_id).first()

    if request.method == 'POST':
        key = request.form.get('key', '').strip()
        value = request.form.get('value', '').strip().replace('\r', '\n')
        flag = request.form.get('flag', 'off')
        note = request.form.get('note', '').strip()

        other = SystemParameter.query.filter(SystemParameter.key == key).first()
        if other and other.id != parameter_id:
            flash('相同的系统参数已存在', 'warning')
        else:
            parameter.key = key
            parameter.value = '\n'.join(value.split('\n'))
            if flag == 'on':
                parameter.flag = True
            else:
                parameter.flag = False
            parameter.note = note

            db.session.add(parameter)
            db.session.commit()
            return  redirect(url_for('auth.system_parameter_page'))

    return render_template('admin/system/system_parameter_edit.html', parameter=parameter)


@auth.route('/upload_file', methods=['GET', 'POST'])
@login_required
def upload_file():
    """附件上传"""

    # 保存附件
    documents = request.files.getlist("file[]")
    current_app.logger.info(documents)
    attachment = []
    if documents:
        upload_path = os.path.join(current_app.config['UPLOAD_FOLDER'],
                                   '%s_%s' % (current_user.username, datetime.now().strftime('%Y-%m-%d-%H-%M-%S')))
        if not os.path.exists(upload_path):
            os.makedirs(upload_path)
        for document in documents:
            current_app.logger.info(document.filename)
            if document.filename:
                file_path = os.path.join(upload_path, document.filename)
                # 删除已存在的
                if os.path.exists(file_path) and os.path.isfile(file_path):
                    os.remove(file_path)
                document.save(file_path)
                attachment.append(file_path)



    return '{"success": "true", "data": "%s"}' % '#'.join(attachment)


<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""
from flask import render_template, redirect, url_for, request, flash, current_app
from flask.ext.login import login_user, logout_user, login_required, current_user

from . import repo
from tms import db
from models import RepositoryServer, Platform, Repository


@repo.route('/repo_server_page')
@login_required
def repo_server_page():
    """"""

    servers = RepositoryServer.query.filter_by(status=True).all()
    current_app.logger.info(servers)
    return render_template('repository/repository_server_page.html',
                           servers=[(index+1, server) for index, server in enumerate(servers)])

<file_sep>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Created by changjie.fan on 2016/11/9
"""
Android 项目开发工程师
"""

from datetime import datetime
import json
import os

from flask import render_template, redirect, url_for, request, flash, current_app, session
from flask.ext.login import login_user, logout_user, login_required, current_user
from flask.ext.mail import Mail, Message
from werkzeug import secure_filename
from functools import update_wrapper

from tms import db, mail
from . import developer
from tms.project.models import Project, Version, VersionHistory, SpecialVersion
from tms.project.androidProjectModules import ProjectAndroid
from tms.auth.models import User, Role
from tms.repository.models import Repository
from tms.utils.email_format import VERSION_SELF_TEST_TEMPLATE
from tms.utils.SendMailPubisher import send_mail_pubisher


@developer.route('/android_dev_my_project')
@login_required
def android_dev_my_project():
    """
    Android 开发工程师的我们的项目
    :return:
    """

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '我的项目', 'url': url_for('developer.android_dev_my_project')})
    session['menu_path'] = ['项目管理', '我的项目']

    # 关注的项目
    follower_project = ProjectAndroid.query.join(User.project_android_f).filter(User.id == current_user.id).all()
    result = []
    projects=ProjectAndroid.query.order_by(ProjectAndroid.id.desc()).all()

    for project_obj in projects:
        if project_obj in follower_project:
            result.insert(0, (project_obj, True))
        else:
            result.append((project_obj, False))


    return render_template('project/android/project_page.html', projects=result)<file_sep>#-*- coding:utf-8 -*-
__author__ = 'changjie.fan'
"""应用初始化"""

import json
from flask import Flask
from flask.sessions import SecureCookieSession, SecureCookieSessionInterface
from flask.ext.mail import Mail
from flask.ext.moment import Moment
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.login import LoginManager
from flask.ext.migrate import Migrate
from config import config


mail = Mail()
moment = Moment()
db = SQLAlchemy()


login_manager = LoginManager()
login_manager.session_protection = 'strong'
login_manager.login_view = 'auth.login'


class JSONSecureCookieSession(SecureCookieSession):
    serialization_method = json


class JSONSecureCookieSessionInterface(SecureCookieSessionInterface):
    session_class = JSONSecureCookieSession


def create_app(config_name):
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app)
    app.session_interface = JSONSecureCookieSessionInterface()

    mail.init_app(app)
    moment.init_app(app)
    db.init_app(app)
    login_manager.init_app(app)
    app.mail = mail
    if not app.debug and not app.testing and not app.config['SSL_DISABLE']:
        from flask.ext.sslify import SSLify
        sslify = SSLify(app)

    # 系统主入口
    from tms.main import main as main_blueprint
    app.register_blueprint(main_blueprint)

    # 用户登陆/注销
    from tms.auth import auth as auth_blueprint
    app.register_blueprint(auth_blueprint, url_prefix='/auth')

    # 项目管理
    from tms.project import project as project_blueprint
    app.register_blueprint(project_blueprint, url_prefix='/project')

    # 工程师模块
    from tms.developer import developer as developer_blueprint
    app.register_blueprint(developer_blueprint, url_prefix='/developer')

    # 任务模块
    from tms.task import task as task_blueprint
    app.register_blueprint(task_blueprint, url_prefix='/task')

    # Git管理模块
    from tms.repository import repo as repo_blueprint
    app.register_blueprint(repo_blueprint, url_prefix='/repo')

    # Qa管理模块
    from tms.qa import qa as qa_blueprint
    app.register_blueprint(qa_blueprint, url_prefix='/qa')

    # scm管理模块
    from tms.scm import scm as scm_blueprint
    app.register_blueprint(scm_blueprint, url_prefix='/scm')

    # 厂商平台、基线/补丁管理模块
    from tms.baseline import baseline as baseline_blueprint
    app.register_blueprint(baseline_blueprint, url_prefix='/baseline')

    # 周报日报
    from tms.report import report as report_blueprint
    app.register_blueprint(report_blueprint, url_prefix='/report')

    # build 编译管理模块
    from tms.build import build as build_blueprint
    app.register_blueprint(build_blueprint, url_prefix='/build')

    # department 部门管理模块
    from tms.departmentMan import department as department_blueprint
    app.register_blueprint(department_blueprint, url_prefix='/department')

    return app
<file_sep>#!/usr/bin/env python
# -*- coding:utf-8 -*-

__author__ = 'changjie.fan'

"""
基于flask-script扩展的管理入口
"""

import os
import sys

reload(sys)
sys.setdefaultencoding('utf-8')


COV = None

if os.environ.get('tms_COVERAGE'):
    import coverage
    COV = coverage.coverage(branch=True, include='tms/*')
    COV.start()

if os.path.exists('.env'):
    print 'Importing environment from env...'
    for line in open('.env'):
        var = line.strip().stplit('=')
        if len(var) == 2:
            os.environ[var[0]] = var[1]

from tms import create_app, db

from tms.auth.models import Role, User

from flask.ext.script import Manager, Shell
from flask.ext.migrate import Migrate, MigrateCommand

app = create_app(os.getenv('tms') or 'default')
manager = Manager(app)
migrate = Migrate(app, db)


def make_shell_context():
    return dict(app=app, db=db, User=User, Role=Role)

manager.add_command("shell", Shell(make_context=make_shell_context))
manager.add_command('db', MigrateCommand)


@manager.command
def test(coverage=False):
    """执行单元测试"""

    if coverage and os.environ.get('tms_COVERAGE'):
        os.environ['tms_COVERAGE'] = '1'
        os.execvp(sys.executable, [sys.executable] + sys.argv)

    import unittest
    tests = unittest.TestLoader.discover('tests')
    unittest.TextTestResult(verbosity=2).run(tests)
    if COV:
        COV.stop()
        COV.start()
        print 'Coverage Summary:'
        COV.report()
        base_dir = os.path.abspath(os.path.dirname(__file__))
        cov_dir = os.path.join(base_dir, 'tmp/coverage')
        COV.html_report(directory=cov_dir)
        print 'HTML version: file://%s/index.html' % cov_dir
        COV.erase()


@manager.command
def profile(length=25, profile_dir=None):
    """
    Start the application under the code profiler.
    在代码分析器中运行应用
    """
    from werkzeug.contrib.profiler import ProfilerMiddleware
    app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions=[length], profile_dir=profile_dir)
    app.run()


@manager.command
def deploy():
    """执行部署任务"""
    from flask.ext.migrate import upgrade

    upgrade()


if __name__ == '__main__':
    manager.run()


<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""
import sys
import commands

from git import Git, Repo, GitCmdObjectDB


class GitRepository:
    """
    Git 仓库操作类
    """
    def __init__(self, repo_path):
        self.repo_path = repo_path
        self.repo = Repo(repo_path, odbt=GitCmdObjectDB)
        self.git = Git(self.repo_path)

    @property
    def heads(self):
        """仓库中的所有分支"""
        return [head.name for head in self.repo.heads]

    @property
    def tags(self):
        """所有tag"""

        return [tag.name for tag in self.repo.tags]

    def log(self, reference_name='', log_format=''):
        """所有提交记录
            默认格式是:
                commit_id/tree/parents/author/author_email/author_date/committer/committer_email/committer_date/message
        """

        if log_format:
            format_str = log_format
        else:
            format_str = "%H<#br@>%P<#br@>%T<#br@>%an<#br@>%ae<#br@>%ad<#br@>%cn<#br@>%ce<#br@>%cd%<#br@>%s%b<####>"

        if not reference_name:
            reference_name = '--all'
        command = 'git --git-dir={0} log --pretty="{1}" {2} --date=iso'.format(self.repo_path, format_str, reference_name)
        print 'command:%s' % command
        status, output = commands.getstatusoutput(command)
        if status:
            print output
            sys.exit(1)
        else:
            commits = [[tmp.strip() for tmp in line.split('<#br@>') if tmp.strip()]
                       for line in output.split('<####>') if line.strip()]
        return commits

    def commits(self, rev_name):
        """
        分支或tag上的所有commit对象
        """
        return [commit_obj for commit_obj in self.repo.commit(rev_name).iter_parents()]

    def diff_commit(self, old_rev, new_rev, log_format=''):
        """比较两个版本或tag获取其commit"""

        if log_format:
            format_str = log_format
        else:
            format_str = "%H<#br@>%P<#br@>%T<#br@>%an<#br@>%ae<#br@>%ad<#br@>%cn<#br@>%ce<#br@>%cd%<#br@>%s%b<####>"

        command = 'git --git-dir={0} log --pretty="{1}" {2}..{3} --date=iso'.format(self.repo_path, format_str, old_rev, new_rev)
        print 'command:%s' % command
        status, output = commands.getstatusoutput(command)
        if status:
            print output
            sys.exit(1)
        else:
            commits = [[tmp.strip() for tmp in line.split('<#br@>') if tmp.strip()]
                       for line in output.split('<####>') if line.strip()]
        return commits

    def log_commit(self, commit_hash, log_format=''):
        """
        显示单个commit的信息
        """

        if log_format:
            format_str = log_format
        else:
            format_str = "%H<#br@>%P<#br@>%T<#br@>%an<#br@>%ae<#br@>%ad<#br@>%cn<#br@>%ce<#br@>%cd%<#br@>%s%b<####>"

        command = 'git --git-dir={0} log -1 --pretty="{1}" {2} --date=iso'.format(self.repo_path, format_str, commit_hash)
        print 'command:%s' % command
        status, output = commands.getstatusoutput(command)
        if status:
            print output
            sys.exit(1)
        else:
            commits = [[tmp.strip() for tmp in line.split('<#br@>') if tmp.strip()]
                       for line in output.split('<####>') if line.strip()]
        return commits[0]


if __name__ == '__main__':
    git_repo = GitRepository('/home/roy/workspace/tup')
    print git_repo.heads
    print git_repo.tags
    for commit in git_repo.log():
        print commit
<file_sep>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Created by changjie.fan on 2016/7/6
"""
Gerrit 引用更新之后的钩子
1. 邮件通知
2. 解析本次push的commit信息
3. 将jira任务和commit 进行关联
"""
import time
import os
import sys
from optparse import OptionParser
import commands
import re

import simplejson as json
import smtplib
# 多个MIME对象的集合
from email.mime.multipart import MIMEMultipart
# MIME文本对象
from email.mime.text import MIMEText
from email.header import Header

# 本机ip地址
import socket
import fcntl
import struct

import MySQLdb
from MySQLdb import connect

import pika

reload(sys)
sys.setdefaultencoding('utf-8')

# tsds数据库信息
db_host = '192.168.33.7'
db_user = 'tup'
db_passwd = 'tup'
db_name = 'tms'

# tsds配置数据库信息
jira_db_host = '172.16.5.95'
jira_db_name = 'jiradb6'
jira_db_user = 'jirauser'
jira_db_passwd = '<PASSWORD>'


def get_ip_address(ifname):
    """获取本机ip地址"""
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        return socket.inet_ntoa(fcntl.ioctl(
            s.fileno(),
            0x8915,  # SIOCGIFADDR
            struct.pack('256s', ifname[:15])
        )[20:24])
    except IOError, e:
        return ''


def send_mail(to_list=[], sub='', content='', appendix_file=''):
    """"send_mail("<EMAIL>","sub","content")
    :param to_list:发给谁
    :param sub:主题
    :param content:内容
    :param appendix_file: 附件
    """

    # 设置服务器、用户名、口令及邮箱的后缀
    mail_host = '192.168.10.8'
    mail_user = '<EMAIL>'
    mail_pass = '<PASSWORD>'
    # sender = config_parser.get("email", "sender").strip("'")
    sender = "<EMAIL>"
    msg_root = MIMEMultipart()
    # 解决中文乱码
    att_text = MIMEText(content, 'html', 'UTF-8')
    # 添加邮件正文
    msg_root.attach(att_text)

    # 发件人
    msg_root['from'] = sender
    # 邮件标题
    msg_root['Subject'] = Header(sub, 'utf-8')
    # 设置时间
    msg_root['Date'] = time.ctime(time.time())
    msg_root['To'] = ';'.join(to_list)

    # 构造附件(附件路径出现中文，会提示编码错误)
    if appendix_file != '':
        rar_file_path = appendix_file
        att = MIMEText(open(rar_file_path, 'rb').read(), 'gbk', 'UTF-8')
        att['Content-Type'] = 'application/octet-stream;name=%s' % Header(rar_file_path, 'UTF-8')
        att['Content-Disposition'] = 'attachment;filename=%s' % Header(rar_file_path, 'UTF-8')
        msg_root.attach(att)

    # 群发邮件
    smtp = smtplib.SMTP(mail_host, 25)
    smtp.login(mail_user, mail_pass)

    smtp.sendmail(sender, to_list, msg_root.as_string())
    # 休眠5秒
    time.sleep(5)
    # 断开与服务器的连接
    smtp.quit()


def get_commit_info(git_path, old_rev, new_rev, local_ip):
    """
        获取提交的详细信息
    :param git_path: 仓库路径
    :param old_rev: 基础Commit Id
    :param new_rev: 当前最新的commit Id
    """

    diff_info = ''
    commit_info = ''
    result = True
    if old_rev != '0'*40 and new_rev != '0'*40:
        command = 'git log -p {0}..{1}'.format(old_rev, new_rev)
        os.chdir(git_path)
        status, info = commands.getstatusoutput(command)
        if status:
            result = False
            send_mail(["<EMAIL>"], "Get Commit Info Error{0}".format(local_ip),
                      "获取仓库的差异信息出错了<br>仓库地址:{0}<br>执行命令:{1}<br>出错信息:<br>Status:{2}<br>{3}".format(git_path, command, status, info))
        else:
            diff_info = info.replace('<', '&lt;').split('\n')
            if len(diff_info) > 2000:
                command = 'git log -p --name-only {0}..{1}'.format(old_rev, new_rev)
                status, info = commands.getstatusoutput(command)
                diff_info = '<br>'.join(info.split('\n'))
            else:
                result = []
                for tmp_line in diff_info:
                    if tmp_line.startswith('-'):
                        tmp_line = '<font color="red">' + tmp_line + '</font>'
                    elif tmp_line.startswith('+'):
                        tmp_line = '<font color="green">' + tmp_line + '</font>'
                    result.append(tmp_line)
                diff_info = '<br>'.join(result)

        # 格式化日志
        command = 'git log --reverse --name-only --date=iso --pretty="<#GIT#>%H<#br#>%T<#br#>%an<#br#>%ae<#br#>%ad<#br#>%cn<#br#>%ce<#br#>%cd<#br#>%s%b</#GIT#>" {0}..{1}'.format(old_rev, new_rev)
        status, info = commands.getstatusoutput(command)
        if status:
            result = False
            print status
            print info
            send_mail(["<EMAIL>"], "Get Commit Info Error{0}".format(local_ip),
                      "获取仓库的差异信息出错了<br>仓库地址:{0}<br>执行命令:{1}<br>出错信息:<br>Status:{2}<br>{3}".format(git_path, command, status, info))
        else:
            commit_info = info

    return result, diff_info, commit_info


def get_task_info(task_key):
    """从JIRA数据库中查询jira任务的信息"""

    query_sql = "SELECT issue.ID, issue.SUMMARY, issue.DESCRIPTION FROM jiraissue issue JOIN project pro WHERE " \
                "issue.issuenum='{0}' AND issue.project=pro.id AND pro.pkey='{1}'".format(task_key.split('-')[1],
                                                                                          task_key.split('-')[0])
    record = ''
    try:
        conn = connect(host=jira_db_host, db=jira_db_name, user=jira_db_user, passwd=jira_db_passwd, charset='utf8')
        cursor = conn.cursor()
        cursor.execute(query_sql)
        record = cursor.fetchone()
        cursor.close()
        conn.close()
    except MySQLdb.Error, e:
        send_mail(["<EMAIL>"], "查询JIRA数据库出错",
                  "post_update.py查询JIRA数据获取任务详细信息出错了<br>SQL:<br>{0}Error:<br>{1}".format(query_sql, e))

    return record


def add_commit(repo_path, project_code_id, commit_info, submitter):
    """
    将日志信息和JIRA任务信息，及任务和commit关联信息保存到数据库中
    :param repo_path:
    :param project_code_id:
    :param commit_info:
    :return:
    """

    # 获取任务JIRA_KEY
    pattern = r'^<(REQ|BUG|FUNC|DEBUG|PATCH|BASELINE|MERGE)><([A-Z]{2,}\-\d+)><(.+)>*'
    rp = re.compile(pattern)

    commit_info = [tmp.strip() for tmp in commit_info.split('<#GIT#>') if tmp.strip()]
    commit_data = []
    # 任务关联的commit
    task_commit = {}
    for commit in commit_info:
        info, change_files = commit.split('</#GIT#>')
        message = [MySQLdb.escape_string(tmp.strip()) for tmp in info.split('<#br#>') if tmp.strip()]
        m = rp.match(message[8])
        task_key = ''
        if m:
            task_key = m.groups()[1]
            if task_key not in task_commit.keys():
                task_commit[task_key] = []
        change_files = '\n'.join([tmp.strip() for tmp in change_files.split('\n') if tmp.strip()])
        commit_data.append([repo_path] + message + [change_files, task_key.split('-')[0], task_key, submitter, 1, project_code_id])

    commit_insert_sql = "INSERT INTO project_branch_commit (repo_path, commit_id, tree_id, auth_name, auth_email, " \
                 "auth_date, commit_name, commit_email, commit_date, message, change_file, project_key, task_key, " \
                 "submitter, activate, project_code_id) VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"

    # jira任务查询语句
    query_task_sql = "SELECT id, task_key FROM project_branch_task WHERE task_key in({0}) AND " \
               "project_code_id={1}".format(','.join(["'%s'" % tmp for tmp in task_commit.keys()]), project_code_id)

    try:
        conn = MySQLdb.connect(host=db_host, db=db_name, user=db_user, passwd=<PASSWORD>, charset='utf8')
        cursor = conn.cursor()

        # 向project_branch_commit表添加commit记录
        for commit in commit_data:
            cursor.executemany(commit_insert_sql, [commit])
            row_id = cursor.lastrowid
            conn.commit()

            if not commit[9].strip().startswith('Merge '):
                task_commit[commit[12]].append(row_id)

        # 数据库中已存在task 任务
        if task_commit.keys():
            cursor.execute(query_task_sql)
            exists_task = {}
            for record in cursor.fetchall():
                exists_task[record[1]] = record[0]

            for task_key, commits in task_commit.items():
                if task_key in exists_task.keys():
                    task_db_id = exists_task[task_key]
                else:
                    record = get_task_info(task_key)
                    if record:
                        insert_task_sql = "INSERT INTO project_branch_task(issue_id, task_key, summary, description, " \
                                          "project_code_id) VALUES (%s, %s, %s, %s, %s)"
                        cursor.executemany(insert_task_sql, [(record[0], task_key, record[1], record[2], project_code_id)])
                    else:
                        insert_task_sql = "INSERT INTO project_branch_task(task_key, project_code_id) VALUES (%s, %s)"
                        cursor.executemany(insert_task_sql, [(task_key, project_code_id)])
                    task_db_id = cursor.lastrowid
                    conn.commit()
                insert_task_commit_sql = "INSERT INTO project_branch_task_commit(project_branch_task_id, project_branch_commit_id) VALUES (%s, %s)"
                task_commits = []
                for commit_id in commits:
                    task_commits.append((task_db_id, commit_id))

                cursor.executemany(insert_task_commit_sql, task_commits)
                conn.commit()

        cursor.close()
        conn.close()
    except MySQLdb.Error, e:
        send_mail(["<EMAIL>"], "添加{0}分支提交记录失败了".format(project_code_id),
                  "Error:{0}<br>Insert into Sql:{1}<br>Data:{2}".format(e, query_task_sql, "<br>".join(commit_info)))


def send_commit_notice_email(repo_path, ref_name, commit_diff_info, local_ip):
    """
    将本次提交的信息邮件通知相关人员
    :param ref_name:
    :param commit_diff_info:
    :return:
    """

    # 查询此分支配置的邮件地址列表
    project_code_id = ''
    email_list = ''
    branch_sql = ''

    try:
        conn = connect(host=db_host, db=db_name, user=db_user, passwd=db_passwd, charset='utf8')
        cursor = conn.cursor()
        branch_sql = "SELECT id, platform_name, commit_notice_email FROM project_code WHERE branch_name='{0}'".format(os.path.basename(ref_name))
        cursor.execute(branch_sql)
        rows = cursor.fetchall()
        if rows and len(rows) > 0:
            for row in rows:
                project_code_id, platform_name, email_list = row[0], row[1], row[2]
                if platform_name and repo_path.startswith(platform_name):
                    break
                else:
                    email_list = ''
        cursor.close()
        conn.close()

    except MySQLdb.Error, e:
        send_mail(["<EMAIL>"], "获取{0}分支的通知邮件列表失败".format(ref_name),
                  "从数据库查询分支的邮件列表失败了<br>{0}<br>{1}<br>{2}".format(local_ip, branch_sql, e))

    if email_list:
        send_mail(email_list.split(';'), '{0}_代码提交通知邮件'.format(ref_name), commit_diff_info)

    return project_code_id


def ref_post_update(old_rev, new_rev, ref_name, repo_path, submitter, local_ip):
    # Git仓库的绝对路径
    git_repo_path = os.path.join('/home/android/review-site/repositories/', repo_path)+'.git'
    # 获取提交的差异日志详细信息
    result, commit_diff_info, commit_info = get_commit_info(git_repo_path, old_rev, new_rev, local_ip)
    if result:
        # 发送提交提醒邮件
        project_code_id = send_commit_notice_email(repo_path, ref_name,
                                                   '<br>'.join([repo_path, '='*80, commit_diff_info]), local_ip)

        if project_code_id:
            # 对提交记录解析，提取JIRA任务并录入数据
            add_commit(repo_path, project_code_id, commit_info, submitter)


def push_consumer():
    """
    发送邮件的消费者
    :return:
    """
    # 建立连接connection到localhost
    con = pika.BlockingConnection(pika.ConnectionParameters('192.168.10.207'))
    # 创建虚拟连接channel
    cha = con.channel()
    # 创建队列anheng
    result = cha.queue_declare(queue='git207_push', durable=True)
    # 创建名为yanfa,类型为fanout的交换机，其他类型还有direct和topic
    cha.exchange_declare(durable=False,
                         exchange='git207',
                         type='direct', )
    # 绑定exchange和queue,result.method.queue获取的是队列名称
    cha.queue_bind(exchange='git207',
                   queue=result.method.queue,
                   routing_key='', )
    # 公平分发，使每个consumer在同一时间最多处理一个message，收到ack前，不会分配新的message
    cha.basic_qos(prefetch_count=1)
    print ' [*] Waiting for messages. To exit press CTRL+C'

    # 定义回调函数
    def callback(ch, method, properties, body):
        print " [x] Received %r" % (body,)
        message = json.loads(body)
        ref_post_update(message['old_rev'], message['new_rev'], message['ref_name'], message['repo_path'],
                        message['uploader'], message['local_ip'])
        ch.basic_ack(delivery_tag=method.delivery_tag)

    cha.basic_consume(callback,
                      queue='git207_push',
                      no_ack=False, )

    cha.start_consuming()


def push_pubisher(message):
    """
    向队列中添加邮件发送任务
    :param message: json格式字串
    :return:
    """

    # 创建连接connection到localhost
    con = pika.BlockingConnection(pika.ConnectionParameters('192.168.10.207'))
    # 创建虚拟连接channel
    cha = con.channel()
    # 创建队列anheng,durable参数为真时，队列将持久化；exclusive为真时，建立临时队列
    result = cha.queue_declare(queue='git207_push', durable=True, exclusive=False)
    # 创建名为yanfa,类型为fanout的exchange，其他类型还有direct和topic，如果指定durable为真，exchange将持久化
    cha.exchange_declare(durable=False,
                         exchange='git207',
                         type='direct', )
    # 绑定exchange和queue,result.method.queue获取的是队列名称
    cha.queue_bind(exchange='git207',
                   queue=result.method.queue,
                   routing_key='', )
    # 公平分发，使每个consumer在同一时间最多处理一个message，收到ack前，不会分配新的message
    cha.basic_qos(prefetch_count=1)

    # 消息持久化指定delivery_mode=2；
    cha.basic_publish(exchange='',
                      routing_key='git207_push',
                      body=message,
                      properties=pika.BasicProperties(
                          delivery_mode=2,
                      ))
    #print '[x] Sent %r' % (message,)

    # 关闭连接
    con.close()

if __name__ == '__main__':
    push_consumer()


<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

"""
project 模块为APK项目模块
定义项目模块的蓝图
"""

from flask import Blueprint


project = Blueprint('project', __name__)
from . import views
from . import projectMyOSViews
from . import androidProjectRepoView
from . import commonIssueView<file_sep>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Created by changjie.fan on 2016/7/12
"""
将分支中的提交信息导入数据中
"""
import sys
import os
import re
import commands
from xml.etree.ElementTree import ElementTree

import MySQLdb
from MySQLdb import connect


reload(sys)
sys.setdefaultencoding('UTF-8')


# myos配置数据库信息
db_host = '192.168.33.7'
db_user = 'tup'
db_passwd = 'tup'
db_name = 'tms_dev'

# tsds配置数据库信息
jira_db_host = '172.16.5.95'
jira_db_name = 'jiradb6'
jira_db_user = 'jirauser'
jira_db_passwd = '<PASSWORD>'


def get_all_git(workspace):
    """"
        读取工程的manifest.xml操作其中的所有仓库
        workspace: 是工程.repo/manifest.xml提货单所在的路径
        comment: 提交说明
    """

    tree = ElementTree()
    element = tree.parse(workspace + '/.repo/manifest.xml')
    default = element.find('default')
    return element.findall('project')


def get_branch_log(git_path, branch_name):
    """
    获取分支上所有的提交记录信息
    :param git_path:
    :param branch_name:
    :return:
    """

    os.chdir(git_path)
    # 格式化日志
    command = 'git log --reverse --name-only --date=iso --pretty="<#GIT#>%H<#br#>%T<#br#>%an<#br#>%ae<#br#>%ad<#br#>%cn<#br#>%ce<#br#>%cd<#br#>%s%b</#GIT#>" {0}'.format(
        branch_name)
    status, info = commands.getstatusoutput(command)
    if status:
        print command
        print info
        sys.exit(1)
    else:
        commit_info = info

    return commit_info


def get_task_info(task_key):
    """从JIRA数据库中查询jira任务的信息"""

    query_sql = "SELECT issue.ID, issue.SUMMARY, issue.DESCRIPTION FROM jiraissue issue JOIN project pro WHERE " \
                "issue.issuenum='{0}' AND issue.project=pro.id AND pro.pkey='{1}'".format(task_key.split('-')[1],
                                                                                          task_key.split('-')[0])
    record = ''
    conn = connect(host=jira_db_host, db=jira_db_name, user=jira_db_user, passwd=jira_db_passwd, charset='utf8')
    cursor = conn.cursor()
    cursor.execute(query_sql)
    record = cursor.fetchone()
    cursor.close()
    conn.close()

    return record


def add_commit(repo_path, project_code_id, commit_info):
    """
    将日志信息和JIRA任务信息，及任务和commit关联信息保存到数据库中
    :param repo_path:
    :param project_code_id:
    :param commit_info:
    :return:
    """

    # 获取任务JIRA_KEY
    pattern = r'^<(REQ|BUG|FUNC|DEBUG|PATCH|BASELINE|MERGE)><([A-Z]{2,}\-\d+)><(.+)>*'
    rp = re.compile(pattern)

    commit_info = [tmp.strip() for tmp in commit_info.split('<#GIT#>') if tmp.strip()]
    commit_data = []
    # 任务关联的commit
    task_commit = {}
    for commit in commit_info:
        info, change_files = commit.split('</#GIT#>')
        message = [MySQLdb.escape_string(tmp.strip()) for tmp in info.split('<#br#>') if tmp.strip()]
        m = rp.match(message[8])
        task_key = ''
        if m:
            task_key = m.groups()[1]
            if task_key not in task_commit.keys():
                task_commit[task_key] = []
        change_files = '\n'.join([tmp.strip() for tmp in change_files.split('\n') if tmp.strip()])
        commit_data.append(
            [repo_path] + message + [change_files, task_key.split('-')[0], task_key, '{0} ({1})'.format(message[5], message[6]), 1, project_code_id])

    commit_insert_sql = "INSERT INTO project_branch_commit (repo_path, commit_id, tree_id, auth_name, auth_email, " \
                        "auth_date, commit_name, commit_email, commit_date, message, change_file, project_key, task_key, " \
                        "submitter, activate, project_code_id) VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"

    # jira任务查询语句
    query_task_sql = "SELECT id, task_key FROM project_branch_task WHERE task_key in({0}) AND " \
                     "project_code_id={1}".format(','.join(["'%s'" % tmp for tmp in task_commit.keys()]),
                                                  project_code_id)

    conn = MySQLdb.connect(host=db_host, db=db_name, user=db_user, passwd=db_passwd, charset='utf8')
    cursor = conn.cursor()

    # 向project_branch_commit表添加commit记录
    for commit in commit_data:
        # print commit_insert_sql
        print commit
        cursor.executemany(commit_insert_sql, [commit])
        row_id = cursor.lastrowid
        print row_id
        conn.commit()
        if commit[12]:
            task_commit[commit[12]].append(row_id)

    # 数据库中已存在task 任务
    if task_commit.keys():
        cursor.execute(query_task_sql)
        exists_task = {}
        for record in cursor.fetchall():
            exists_task[record[1]] = record[0]

        for task_key, commits in task_commit.items():
            if task_key in exists_task.keys():
                task_db_id = exists_task[task_key]
            else:
                record = get_task_info(task_key)
                if record:
                    insert_task_sql = "INSERT INTO project_branch_task(issue_id, task_key, summary, description, " \
                                      "project_code_id) VALUES (%s, %s, %s, %s, %s)"
                    cursor.executemany(insert_task_sql, [(record[0], task_key, record[1], record[2], project_code_id)])
                else:
                    insert_task_sql = "INSERT INTO project_branch_task(task_key, project_code_id) VALUES (%s, %s)"
                    cursor.executemany(insert_task_sql, [(task_key, project_code_id)])
                task_db_id = cursor.lastrowid
                conn.commit()
            insert_task_commit_sql = "INSERT INTO project_branch_task_commit(project_branch_task_id, project_branch_commit_id) VALUES (%s, %s)"
            task_commits = []
            for commit_id in commits:
                task_commits.append((task_db_id, commit_id))

            cursor.executemany(insert_task_commit_sql, task_commits)
            conn.commit()

    cursor.close()
    conn.close()


def main(platform_name, branch_name, project_code_id):

    current_path = os.getcwd()
    projects = get_all_git(current_path)
    for project in projects:
        if project.attrib['name'].startswith(platform_name):
            commit_info = get_branch_log(os.path.join(current_path, project.attrib['path']), branch_name)
            add_commit(project.attrib['name'], project_code_id, commit_info)


if __name__ == '__main__':
    """
    git服务器上平台名称
    分支名称remotes/tinno/
    数据库里货单的id
    """
    main(sys.argv[1], sys.argv[2], sys.argv[3])<file_sep>{% extends "developer/developer_base.html" %}
{% block title %}提交记录详细信息{% endblock %}
{% block header %}
    {{ super() }}
    <link href="{{ url_for('static', filename='assets/global/plugins/bootstrap-select/bootstrap-select.min.css') }}"
          rel="stylesheet" type="text/css"/>
    <link href="{{ url_for('static', filename='assets/global/plugins/select2/select2.css') }}" rel="stylesheet"
          type="text/css"/>
    <link href="{{ url_for('static', filename='assets/global/plugins/bootstrap-duallistbox/bootstrap-duallistbox.min.css') }}"
          rel="stylesheet" type="text/css"/>
    <link href="{{ url_for('static', filename='assets/global/plugins/bootstrap-datetimepicker/css/bootstrap-datetimepicker.min.css') }} "
          rel="stylesheet" type="text/css"/>
{% endblock %}
{% block page_content %}
    <div class="row">
        <div class="col-md-12">
            <!-- BEGIN PORTLET-->
            <div class="portlet box yellow-crusta">
                <div class="portlet-title">
                    <div class="caption">
                        <i class="fa fa-gift"></i>Commit详细信息
                    </div>
                </div>
                <div class="portlet-body form">
                    <!-- BEGIN FORM-->
                    <form id="project_form" action="" class="horizontal-form" method="POST">
                        <div class="form-body">
                            <label><span class="label label-success"
                                         style="font-size: medium">Repository Path:</span>{{ commit.repo_path }}</label><br>
                            <label><span class="label label-success"
                                         style="font-size: medium">commit:</span>{{ commit.commit_id }}</label><br>
                            <label><span class="label label-primary"
                                         style="font-size: medium">Author:</span> {{ commit.auth_name }}
                                &lt;{{ commit.auth_email }}</label><br>
                            <label><span class="label label-primary"
                                         style="font-size: medium">Auth Date:</span> {{ commit.auth_date }}</label><br>
                            <label><span class="label label-primary"
                                         style="font-size: medium">Committer:</span> {{ commit.commit_name }}
                                &lt;{{ commit.commit_email }}&gt;</label><br>
                            <label><span class="label label-primary"
                                         style="font-size: medium">Commit Date:</span> {{ commit.commit_date }}
                            </label><br>
                            <label><span class="label label-info"
                                         style="font-size: medium">Message:</span> {{ commit.message }}
                            </label><br>
                            <label><span class="label label-default"
                                         style="font-size: medium">Change Files:</span><br>
                                {%  for line in commit.change_file.split('\n')  %}
                                   {{ line }} <br>
                                {% endfor %}
                            </label><br>
                        </div>
                    </form>
                </div>
            </div>
            <!-- END PORTLET-->
        </div>
    </div>
{% endblock %}

{% block script %}
    <script src="{{ url_for('static', filename='assets/global/plugins/bootstrap-select/bootstrap-select.min.js') }}"></script>
    <script src="{{ url_for('static', filename='assets/global/plugins/select2/select2.min.js') }}"></script>
    <script src="{{ url_for('static', filename='assets/global/plugins/bootstrap-duallistbox/jquery.bootstrap-duallistbox.min.js') }}"></script>
    <script src="{{ url_for('static', filename='assets/global/plugins/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js') }}"
            type="text/javascript"></script>
    <script src="{{ url_for('static', filename='assets/global/plugins/bootbox/bootbox.min.js') }}"
            type="text/javascript"></script>
    <script type="text/javascript">
        $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
    </script>
{% endblock %}<file_sep>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Created by changjie.fan on 2016/8/25
"""
"""
from flask import Blueprint

baseline = Blueprint('baseline', __name__)
from . import platformViews
<file_sep>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Created by changjie.fan on 2016/8/25
"""
"""
from flask import render_template, redirect, url_for, request, flash, current_app, session
from flask.ext.login import login_user, logout_user, login_required, current_user
from flask.ext.mail import Mail, Message
from werkzeug import secure_filename
from functools import update_wrapper

from . import baseline
from tms import db
from models import BPlatform


@baseline.route('/platform_page')
@login_required
def platform_page():
    """系统参数页面"""

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': u'平台管理', 'url': url_for('baseline.platform_page')})
    session['menu_path'] = [u'基线管理', u'平台管理']

    platforms = BPlatform.query.all()

    return render_template('baseline/platform/platform_page.html', platforms=platforms)


@baseline.route('/add_platform', methods=['GET', 'POST'])
@login_required
def add_platform():
    """添加平台"""

    platform_obj = BPlatform()

    if request.method == 'POST':
        name = request.form.get('name', '').strip()
        flag = request.form.get('flag', 'off')
        note = request.form.get('note', '').strip()

        other = BPlatform.query.filter(BPlatform.name == name).first()
        if other:
            flash('相同平台名称已存在', 'warning')
        else:
            platform_obj.name = name
            if flag == 'on':
                platform_obj.flag = True
            else:
                platform_obj.flag = False
            platform_obj.note = note

            db.session.add(platform_obj)
            db.session.commit()
            return  redirect(url_for('baseline.platform_page'))

    return render_template('baseline/platform/platform_edit.html', platform=platform_obj)



@baseline.route('/edit_platform/<int:platform_id>', methods=['GET', 'POST'])
@login_required
def edit_platform(platform_id):
    """修改平台信息"""

    platform_obj = BPlatform.query.filter_by(id=platform_id).first()

    if request.method == 'POST':
        name = request.form.get('name', '').strip()
        flag = request.form.get('flag', 'off')
        note = request.form.get('note', '').strip()

        other = BPlatform.query.filter(BPlatform.name == name).first()
        if other and other.id != platform_id:
            flash('相同平台名称已存在', 'warning')
        else:
            platform_obj.name = name
            if flag == 'on':
                platform_obj.flag = True
            else:
                platform_obj.flag = False
            platform_obj.note = note

            db.session.add(platform_obj)
            db.session.commit()
            return redirect(url_for('baseline.platform_page'))

    return render_template('baseline/platform/platform_edit.html', platform=platform_obj)<file_sep>(function () {
    var h = window.AmCharts;
    h.AmPieChart = h.Class({inherits: h.AmSlicedChart, construct: function (d) {
        this.type = "pie";
        h.AmPieChart.base.construct.call(this, d);
        this.cname = "AmPieChart";
        this.pieBrightnessStep = 30;
        this.minRadius = 10;
        this.depth3D = 0;
        this.startAngle = 90;
        this.angle = this.innerRadius = 0;
        this.startRadius = "500%";
        this.pullOutRadius = "20%";
        this.labelRadius = 20;
        this.labelText = "[[title]]: [[percents]]%";
        this.balloonText = "[[title]]: [[percents]]% ([[value]])\n[[description]]";
        this.previousScale = 1;
        this.adjustPrecision = !1;
        h.applyTheme(this, d, this.cname)
    }, drawChart: function () {
        h.AmPieChart.base.drawChart.call(this);
        var d = this.chartData;
        if (h.ifArray(d)) {
            if (0 < this.realWidth && 0 < this.realHeight) {
                h.VML && (this.startAlpha = 1);
                var f = this.startDuration, c = this.container, b = this.updateWidth();
                this.realWidth = b;
                var m = this.updateHeight();
                this.realHeight = m;
                var e = h.toCoordinate, k = e(this.marginLeft, b), a = e(this.marginRight, b), v = e(this.marginTop, m) + this.getTitleHeight(), n = e(this.marginBottom, m), y, z, g, x = h.toNumber(this.labelRadius), q =
                    this.measureMaxLabel();
                q > this.maxLabelWidth && (q = this.maxLabelWidth);
                this.labelText && this.labelsEnabled || (x = q = 0);
                y = void 0 === this.pieX ? (b - k - a) / 2 + k : e(this.pieX, this.realWidth);
                z = void 0 === this.pieY ? (m - v - n) / 2 + v : e(this.pieY, m);
                g = e(this.radius, b, m);
                g || (b = 0 <= x ? b - k - a - 2 * q : b - k - a, m = m - v - n, g = Math.min(b, m), m < b && (g /= 1 - this.angle / 90, g > b && (g = b)), m = h.toCoordinate(this.pullOutRadius, g), g = (0 <= x ? g - 1.8 * (x + m) : g - 1.8 * m) / 2);
                g < this.minRadius && (g = this.minRadius);
                m = e(this.pullOutRadius, g);
                v = h.toCoordinate(this.startRadius, g);
                e = e(this.innerRadius, g);
                e >= g && (e = g - 1);
                n = h.fitToBounds(this.startAngle, 0, 360);
                0 < this.depth3D && (n = 270 <= n ? 270 : 90);
                n -= 90;
                b = g - g * this.angle / 90;
                for (k = q = 0; k < d.length; k++)a = d[k], !0 !== a.hidden && (q += h.roundTo(a.percents, this.pf.precision));
                q = h.roundTo(q, this.pf.precision);
                this.tempPrec = NaN;
                this.adjustPrecision && 100 != q && (this.tempPrec = this.pf.precision + 1);
                for (k = 0; k < d.length; k++)if (a = d[k], !0 !== a.hidden && 0 < a.percents) {
                    var r = 360 * a.percents / 100, q = Math.sin((n + r / 2) / 180 * Math.PI), A = -Math.cos((n + r / 2) / 180 * Math.PI) * (b /
                        g), p = this.outlineColor;
                    p || (p = a.color);
                    var B = this.alpha;
                    isNaN(a.alpha) || (B = a.alpha);
                    p = {fill: a.color, stroke: p, "stroke-width": this.outlineThickness, "stroke-opacity": this.outlineAlpha, "fill-opacity": B};
                    a.url && (p.cursor = "pointer");
                    p = h.wedge(c, y, z, n, r, g, b, e, this.depth3D, p, this.gradientRatio, a.pattern);
                    h.setCN(this, p, "pie-item");
                    h.setCN(this, p.wedge, "pie-slice");
                    h.setCN(this, p, a.className, !0);
                    this.addEventListeners(p, a);
                    a.startAngle = n;
                    d[k].wedge = p;
                    0 < f && (this.chartCreated || p.setAttr("opacity", this.startAlpha));
                    a.ix = q;
                    a.iy = A;
                    a.wedge = p;
                    a.index = k;
                    if (this.labelsEnabled && this.labelText && a.percents >= this.hideLabelsPercent) {
                        var l = n + r / 2;
                        360 < l && (l -= 360);
                        var t = x;
                        isNaN(a.labelRadius) || (t = a.labelRadius);
                        var r = y + q * (g + t), B = z + A * (g + t), C, w = 0;
                        if (0 <= t) {
                            var D;
                            90 >= l && 0 <= l ? (D = 0, C = "start", w = 8) : 90 <= l && 180 > l ? (D = 1, C = "start", w = 8) : 180 <= l && 270 > l ? (D = 2, C = "end", w = -8) : 270 <= l && 360 > l && (D = 3, C = "end", w = -8);
                            a.labelQuarter = D
                        } else C = "middle";
                        var l = this.formatString(this.labelText, a), u = this.labelFunction;
                        u && (l = u(a, l));
                        u = a.labelColor;
                        u || (u = this.color);
                        "" !== l && (l = h.wrappedText(c, l, u, this.fontFamily, this.fontSize, C, !1, this.maxLabelWidth), h.setCN(this, l, "pie-label"), h.setCN(this, l, a.className, !0), l.translate(r + 1.5 * w, B), l.node.style.pointerEvents = "none", a.tx = r + 1.5 * w, a.ty = B, 0 <= t ? (t = l.getBBox(), u = h.rect(c, t.width + 5, t.height + 5, "#FFFFFF", .005), u.translate(r + 1.5 * w + t.x, B + t.y), a.hitRect = u, p.push(l), p.push(u)) : this.freeLabelsSet.push(l), a.label = l);
                        a.tx = r;
                        a.tx2 = r + w;
                        a.tx0 = y + q * g;
                        a.ty0 = z + A * g
                    }
                    r = e + (g - e) / 2;
                    a.pulled && (r += this.pullOutRadiusReal);
                    a.balloonX = q * r + y;
                    a.balloonY =
                        A * r + z;
                    a.startX = Math.round(q * v);
                    a.startY = Math.round(A * v);
                    a.pullX = Math.round(q * m);
                    a.pullY = Math.round(A * m);
                    this.graphsSet.push(p);
                    (0 === a.alpha || 0 < f && !this.chartCreated) && p.hide();
                    n += 360 * a.percents / 100
                }
                0 < x && !this.labelRadiusField && this.arrangeLabels();
                this.pieXReal = y;
                this.pieYReal = z;
                this.radiusReal = g;
                this.innerRadiusReal = e;
                0 < x && this.drawTicks();
                this.initialStart();
                this.setDepths()
            }
            (d = this.legend) && d.invalidateSize()
        } else this.cleanChart();
        this.dispDUpd();
        this.chartCreated = !0
    }, setDepths: function () {
        var d =
            this.chartData, f;
        for (f = 0; f < d.length; f++) {
            var c = d[f], b = c.wedge, c = c.startAngle;
            0 <= c && 180 > c ? b.toFront() : 180 <= c && b.toBack()
        }
    }, arrangeLabels: function () {
        var d = this.chartData, f = d.length, c, b;
        for (b = f - 1; 0 <= b; b--)c = d[b], 0 !== c.labelQuarter || c.hidden || this.checkOverlapping(b, c, 0, !0, 0);
        for (b = 0; b < f; b++)c = d[b], 1 != c.labelQuarter || c.hidden || this.checkOverlapping(b, c, 1, !1, 0);
        for (b = f - 1; 0 <= b; b--)c = d[b], 2 != c.labelQuarter || c.hidden || this.checkOverlapping(b, c, 2, !0, 0);
        for (b = 0; b < f; b++)c = d[b], 3 != c.labelQuarter || c.hidden || this.checkOverlapping(b,
            c, 3, !1, 0)
    }, checkOverlapping: function (d, f, c, b, h) {
        var e, k, a = this.chartData, v = a.length, n = f.label;
        if (n) {
            if (!0 === b)for (k = d + 1; k < v; k++)a[k].labelQuarter == c && (e = this.checkOverlappingReal(f, a[k], c)) && (k = v); else for (k = d - 1; 0 <= k; k--)a[k].labelQuarter == c && (e = this.checkOverlappingReal(f, a[k], c)) && (k = 0);
            !0 === e && 100 > h && (e = f.ty + 3 * f.iy, f.ty = e, n.translate(f.tx2, e), f.hitRect && (n = n.getBBox(), f.hitRect.translate(f.tx2 + n.x, e + n.y)), this.checkOverlapping(d, f, c, b, h + 1))
        }
    }, checkOverlappingReal: function (d, f, c) {
        var b = !1, m = d.label,
            e = f.label;
        d.labelQuarter != c || d.hidden || f.hidden || !e || (m = m.getBBox(), c = {}, c.width = m.width, c.height = m.height, c.y = d.ty, c.x = d.tx, d = e.getBBox(), e = {}, e.width = d.width, e.height = d.height, e.y = f.ty, e.x = f.tx, h.hitTest(c, e) && (b = !0));
        return b
    }})
})();<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

"""通知邮件模板"""

# 通过开发自测后的，版本通知邮件
VERSION_SELF_TEST_TEMPLATE = '<br>'.join([
    'Dear All:<br>',
    '{0}项目,{1}版本发布如下:<br>',
    '<h4>版本号:</h4>{2}',
    '<h4>升级建议:</h4>{3} 注 (修改重要问题，必须升级的话，该值为Must)',
    '<h4>FTP地址:</h4>{4}',
    '<h4>JenkinsURL:</h4>{5}',
    '<h4>版本说明:</h4>{6}',
    '<h4>版本变更内容:</h4>{7}',
    '-'*100,
    '<br>',
    'Dear 袁成,',
    '',
    '请将版本上传至深圳SVN',
    '',
    '谢谢!',
    '<br>',
    'Best Regards',
    '{8}|{9}',
    '力争取胜 | 快速行动 | 团队精神',
])

# 测试失败后的，版本通知邮件
TEST_FAILURE_TEMPLATE = '<br>'.join([
    'Dear All:',
    '',
    '{0}项目,{1}版本发布如下:',
    '<br>',
    '<h4>版本号:</h4>{2}',
    '<h4>升级建议:</h4>{3} 注 (修改重要问题，必须升级的话，该值为Must)',
    '<h4>测试结果:</h4><font color="red">未达到发放要求</font><br>'
    '<h4>版本缺陷:</h4>{4}',
    '<h4>',
    'Best regards',
    '{5}|{6}',
    '测试部(南京)<br>',
    '开放 | 尊重 | 分享 | 创新'
])

# 测试通过后的，版本通知邮件
TEST_PASS_TEMPLATE = '<br>'.join([
    'Dear All:',
    '',
    '{0}项目,{1}版本发布如下:',
    '<br>',
    '<h4>版本号:</h4>{2}',
    '<h4>升级建议:</h4>{3} 注 (修改重要问题，必须升级的话，该值为Must)',
    '<h4>测试结果:</h4><font color="green">测试通过</font><br>'
    '<h4>版本缺陷:</h4>{4}',
    '<br>',
    'Best regards',
    '{5}|{6}',
    '测试部(南京)<br>',
    '开放 | 尊重 | 分享 | 创新'
])


# 版本迭代发放的通知邮件
RELEASE_TEMPLATE = '<br>'.join([
    'Dear ALL:<br>',
    '{0}项目, {1}的{2}版本经软件测试部验证和确认，已完成当前阶段功能的开发。通过公司独立应用测试标准，目前SMI为{3}，',
    '可交接给产品项目集成，谢谢。',
    '<h4>升级建议:</h4>{4} 注 (修改重要问题，必须升级的话，该值为Must)',
    '<h4>文件全称:</h4>{5}',
    '<h4>应用包名:</h4>{6}',
    '<h4>Release软件存放:</h4>{7}',
    '<h4>发放声明:</h4>{8}',
    '<h4>遗留问题列表:</h4>{9}<br><br>',
    'Best regards',
    '{10}|{11}',
    '测试部(南京)<br>',
    '开放 | 尊重 | 分享 | 创新'
])

# 版本DCC发放的通知邮件
DCC_RELEASE_TEMPLATE = '<br>'.join([
    'Dear ALL:<br>',
    '{0}项目, {1}的{2}版本经软件测试部验证和确认，已完成当前阶段功能的开发。通过公司独立应用测试标准，目前SMI为{3}，',
    '可交接给产品项目集成，谢谢。',
    '<h4>升级建议</h4>{4} 注 (修改重要问题，必须升级的话，该值为Must)',
    '<h4>文件全称:</h4>{5}',
    '<h4>应用包名:</h4>{6}',
    '<h4>Release软件存放:</h4>{7}',
    '<h4>发放声明:</h4>{8}',
    '<h4>{9}独立应用已做以下适配:</h4><br>{10}',
    '<h4>遗留问题列表:</h4>{11}<br><br>',
    'Best regards',
    '{12}|{13}',
    '测试部(南京)<br>',
    '开放 | 尊重 | 分享 | 创新'
])

# 版本特别发放的通知邮件
SPECIAL_VERSION_TEMPLATE = '<br>'.join([
    'Dear ALL:<br>',
    '{0}项目, {1}的{2}版本经软件测试部验证和确认，已完成当前阶段功能的开发。通过公司独立应用测试标准，目前SMI为{3}，',
    '可交接给产品项目集成，谢谢。',
    '<h4>升级建议:</h4>{4} 注 (修改重要问题，必须升级的话，该值为Must)',
    '<h4><font color="red">注意: {5}</font></h4>'
    '<h4>文件全称:</h4>{6}',
    '<h4>应用包名:</h4>{7}',
    '<h4>Release软件存放:</h4>{8}',
    '<h4>发放声明:</h4>{9}',
    '<h4>遗留问题列表:</h4>{10}<br><br>',
    'Best regards',
    '{11}|{12}',
    '测试部(南京)<br>',
    '开放 | 尊重 | 分享 | 创新'
])


# 研发版本通知邮件
VERSION_TEST_TEMPLATE = '<br>'.join([
    'Dear All:',
    '',
    '{0}项目,{1}版本发布如下:',
    '<br>',
    '<h4>版本号:</h4>{2}',
    '<h4>版本SMI值:</h4>{3}',
    '<h4>版本缺陷:</h4>{4}',
    '<h4>版本测试说明:</h4>{5}',
    '<br>',
    'Best regards',
    '{6}|{7}',
    '测试部(南京)<br>',
    '开放 | 尊重 | 分享 | 创新'
])<file_sep>#-*- coding:utf-8 -*-
__author__ = 'changjie.fan'
"""
用户/角色/权限 数据库模块
"""
from datetime import datetime
import hashlib

from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from werkzeug.security import generate_password_hash, check_password_hash
from flask.ext.login import UserMixin, AnonymousUserMixin
from flask import current_app

from tms.exceptions import ValidationError
from tms import db, login_manager


class Role(db.Model):
    """角色"""

    __tablename__ = 'tms_role'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    default = db.Column(db.Boolean, default=False, index=True)
    permission = db.Column(db.Integer)
    index_page = db.Column(db.String(64))
    menu = db.Column(db.String(200))

    @staticmethod
    def insert_roles():
        roles = {
            'dev_engineer':(),
            'test_engineer':(),
            'scm': (),
            'admin': (),
        }
        for r in roles:
            role = Role.query.filter_by(name=r).first()
            if role is None:
                role = Role(name=r)
            db.session.add(role)
        db.session.commit()

    def __repr__(self):
        return '<Role %r>' % self.name


class User(UserMixin, db.Model):
    """用户"""

    __tablename__ = 'tms_user'

    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(64), unique=True, index=True)
    username = db.Column(db.String(64), unique=True, index=True)
    display_name = db.Column(db.String(64), nullable=True)
    role_id = db.Column(db.Integer, db.ForeignKey('tms_role.id'))
    role = db.relationship('Role', backref=db.backref('user', order_by=id))
    password_hash = db.Column(db.String(128))
    confirmed = db.Column(db.Boolean, default=False)
    location = db.Column(db.String(64))
    member_since = db.Column(db.DateTime(), default=datetime.now())
    last_seen = db.Column(db.DateTime(), default=datetime.now())
    avatar_hash = db.Column(db.String(32))
    activate = db.Column(db.Boolean, default=True)
    department_id = db.Column(db.Integer, db.ForeignKey('tms_department.id'))
    department = db.relationship('Department', backref=db.backref('user', order_by=id))


    @staticmethod
    def generate_fake(count=100):
        """创建测试用户"""
        from sqlalchemy.exc import IntegrityError
        from random import seed
        import forgery_py

        seed()
        for i in range(count):
            u = User(
                email=forgery_py.internet.email_address(),
                username=forgery_py.internet.user_name(True),
                password=<PASSWORD>(),
                confirmed=True,
                name=forgery_py.name.full_name(),
                member_since=forgery_py.date.date(True)
            )
            db.session.add(u)
            try:
                db.session.commit()
            except IntegrityError:
                db.session.rollback()

    @staticmethod
    def __init__(self, **kwargs):
        super(User, self).__init__(**kwargs)
        if self.role is None:
            if self.email == current_app.config['TMS_ADMIN']:
                self.role = Role.query.filter_by(name='admin').first()
            if self.role is None:
                self.role = Role.query.filter_by(default=True).first()
        if self.email is not None and self.avatar_hash is None:
            self.avatar_hash = hashlib.md5(self.email.encode('utf-8')).hexdigest()

    @property
    def password(self):
        raise AttributeError('password is not a readable attribute')

    @password.setter
    def password(self, password):
        self.password_hash = generate_password_hash(password)

    def reset_password(self, token, new_password):
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(s)
        except Exception, e:
            return False
        if data.get('reset') != self.id:
            return False
        self.password = <PASSWORD>
        db.session.add(self)
        return True

    def verify_password(self, password):
        return check_password_hash(self.password_hash, password)

    def generate_confirmation_token(self, expiration=3600):
        """生成验证连接有效期是1小时"""
        s = Serializer(current_app.config['SECRET_KEY'], expiration)
        return s.dumps({'reset': self.id})

    def generate_email_change_token(self, new_email, expiration=3600):
        s = Serializer(current_app.config['SECRET_KEY'], expiration)
        return s.dumps({'change_email': self.id, 'new_email': new_email})

    def change_email(self, token):
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(token)
        except Exception, e:
            return False
        if data.get('change_email') != self.id:
            return False
        new_email = data.get('new_email')
        if new_email is None:
            return False
        if self.query.filter_by(email=new_email) is not None:
            return False

        self.email = new_email
        self.avatar_hash = hashlib.md5(self.email.encode('utf-8')).hexdigest()
        db.session.add(self)
        return True

    def can(self, permissions):
        pass

    def is_administrator(self):
        pass

    def ping(self):
        self.last_seen = datetime.now()
        db.session.add(self)

    def genrate_auth_token(self, expiration):
        s = Serializer(current_app.config['SECRET_KEY'], expires_in=expiration)
        return s.dumps({'id': self.id}).decode('ascii')

    @staticmethod
    def verify_auth_token(token):
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(token)
        except Exception, e:
            return None

        return User.query.get(data['id'])

    def __repr__(self):
        return '<User %r>' % self.username


class AnonymousUser(AnonymousUserMixin):
    def can(self, permissions):
        return False

    def is_administrator(self):
        return False

login_manager.anonymous_user = AnonymousUser


@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))


class SystemMenu(db.Model):
    """系统菜单"""

    __tablename__ = 'tms_menu'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64))
    action = db.Column(db.String(64))
    activate = db.Column(db.Boolean, default=True)
    note = db.Column(db.String(100))
    order = db.Column(db.Integer)
    # 菜单样式
    style = db.Column(db.String(300), nullable=True)

    parent_id = db.Column(db.Integer, db.ForeignKey('tms_menu.id'), nullable=True)
    parent = db.relation('SystemMenu', uselist=False, remote_side=[id],
                         backref=db.backref('children', order_by=order))

    def __repr__(self):
        return '<SystemMenu %r>' % self.name


class SystemLog(db.Model):
    """系统日志"""

    __tablename__ = 'tms_log'

    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(50))
    option = db.Column(db.String(300))
    option_time = db.Column(db.DateTime)

    def __repr__(self):
        return '<SystemLog %r>' % self.username


class SystemParameter(db.Model):
    """系统参数表"""

    __tablename__ = 'tms_paramter'

    id = db.Column(db.Integer, primary_key=True)
    key = db.Column(db.String(64))
    value = db.Column(db.Text)
    flag = db.Column(db.Boolean, default=True)
    note = db.Column(db.String(255))

    def __repr__(self):
        return '<SystemParameter %r>' % self.key
<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""
import os


class MenuObject:
    def __init__(self, id, name, url, style):
        self.id = id
        self.name = name
        self.url = url
        self.style = style
        self.children = []


def create_menu(menus):
    """创建默认菜单"""

    parents = []
    for menu in menus:
        if not menu.parent_id:
            parents.append({'id': menu.id, 'name': menu.name, 'url': menu.action, 'style': menu.style,
                            'children': []})

    for menu in menus:
        if menu.parent_id:
            for parent in parents:
                if parent['id'] == menu.parent_id:
                    parent['children'].append({'id': menu.id, 'name': menu.name, 'url': menu.action,
                                               'style': menu.style, 'children': []})

    return parents


def getOrder(menu):
    if menu:
        return menu.order
    else:
        return 0<file_sep>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Created by changjie.fan on 2016/7/6
"""
"""<file_sep>/**
 * Created by changjie.fan on 2015/11/10.
 */
$(function(){
    /*用户设置*/
    $('#email_submit').bind('click',function(){
        var data = {
            data: JSON.stringify({
                'email': $('#email').val()
            })
        };
        $.ajax({
            url: '/auth/change_email',
            type: 'POST',
            data: data,
            success: function(msg){
                alert(msg);
            }
        });
    });
});<file_sep>#!/usr/bin/env python
# -*- coding:utf-8 -*-

"""
Created on 2016-2-27
@author: changjie.fan
将当天所有commit的信息导入到数据库中

"""
import logging
import sys
import os
import time
from datetime import datetime

from DBOption import DBOption
from GitRepository import GitRepository


def add_commit_reference(logger):

    # mysql操作对象初始化
    db_url = {'host': '192.168.33.7', 'db_name': 'tms', 'username': 'tup', 'password': 'tup'}
    db_option = DBOption(db_url, 'mysql')
    db_option.db_connect()

    record_sql = "SELECT history.id, history.repository_path, history.push_type, history.commits, " \
                 "history.reference_name, repo_server.root_path, repo_server.backup_server_path, repo.id " \
                 "FROM repo_push_history AS history JOIN repo_server ON history.server_ip=repo_server.ip_address " \
                 "JOIN repo_repo AS repo ON repo.path=history.repository_path WHERE history.status=1 and " \
                 "history.server_ip IN('192.168.33.9', '192.168.33.13') LIMIT 100 "

    new_references = []
    new_commits = []
    for record in db_option.fetch_all(record_sql):
        logger.info(record[7])
        row_id, repo_path, push_type, commits, reference_name, root_path, backup_server_path, repo_id = \
            record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7]

        if push_type == 'create_reference':
            if reference_name.strip().startswith('refs/heads/'):
                new_references.append((reference_name, 1, repo_id))
            else:
                new_references.append((reference_name, 0, repo_id))
        elif push_type == 'update_reference':
            repo_path = repo_path.replace(root_path, backup_server_path)
            repo_obj = GitRepository(repo_path)
            if commits.find('/') > 0:
                for commit in repo_obj.diff_commit(commits.split('/')[0], commits.split('/')[1]):
                    if len(commit) == 9:
                        new_commits.append((commit[0], '', commit[1], commit[2], commit[3], commit[4],
                                            commit[5].split('+')[0], commit[6], commit[7], commit[8].split('+')[0],
                                            repo_id))
                    else:
                        new_commits.append((commit[0], commit[1], commit[2], commit[3], commit[4],
                                            commit[5].split('+')[0], commit[6], commit[7], commit[8].split('+')[0],
                                            commit[9], repo_id))

            else:
                commit = repo_obj.log_commit(commits)
                if len(commit) == 9:
                    new_commits.append((commit[0], '', commit[1], commit[2], commit[3], commit[4],
                                        commit[5].split('+')[0], commit[6], commit[7], commit[8].split('+')[0],
                                        repo_id))
                else:
                    new_commits.append((commit[0], commit[1], commit[2], commit[3], commit[4], commit[5].split('+')[0],
                                        commit[6], commit[7], commit[8].split('+')[0], commit[9], repo_id))

        elif push_type == 'delete_reference':
            update_reference_sql = "UPDATE repo_refs set status=1, push_id={0} WHERE repository_id={1} " \
                                   "and name='{2}'".format(row_id, repo_id, reference_name)
            db_option.insert_one(update_reference_sql)

        update_sql = "UPDATE repo_push_history set status=3, import_time='{0}' " \
                     "WHERE id={1}".format(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), row_id)
        db_option.insert_one(update_sql)

    # 新增的分支和tag添加入数据库
    if new_references:
        insert_refs_sql = "INSERT INTO repo_refs (name, category, repository_id) VALUES (%s, %s, %s)"
        db_option.insert_all(insert_refs_sql, new_references)

    # 新增的commit添加入数据库
    if new_commits:
        insert_commit_sql = "INSERT INTO repo_commit (commit_hash, parent_hash, tree_hash, author_name, author_email, " \
                            "author_time, committer_name, committer_email, commit_time, message, repository_id) " \
                            "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"
        print new_commits
        db_option.insert_all(insert_commit_sql, new_commits)

    db_option.close()

if __name__ == "__main__":
    log_file = os.path.join('/var/log/git_backup', datetime.now().strftime('%Y-%m-%d')+'.log')
    logging.basicConfig(
        level=logging.INFO,
        format='',
        datefmt='%Y-%m-%d/%H:%M:%S',
        filename=log_file,
        filemode='a'
        )

    file_handler = logging.FileHandler(log_file, 'a')
    formatter = logging.Formatter('%(asctime)s %(name)s[line:%(lineno)d]:%(levelname)s %(message)s')
    file_handler.setFormatter(formatter)

    console_handler = logging.StreamHandler()
    console_handler.setFormatter(formatter)
    logging.root.addHandler(console_handler)
    logging.root.addHandler(file_handler)
    logger = logging.getLogger('Git')

    add_commit_reference(logger)
    while True:
        add_commit_reference(logger)
        time.sleep(200)
<file_sep>#!/bin/bash

python manage.py runserver -t 192.168.33.64 -p 5000
<file_sep>/**
 * Created by changjie.fan on 2016/1/22.
 */
$("#department_manage_table").treetable({expandable: true});

// Highlight selected row
$("#department_manage_table tbody").on("mousedown", "tr", function () {
    $(".selected").not(this).removeClass("selected");
    $(this).toggleClass("selected");
});

// Drag & Drop Example Code
$("#department_manage_table .file, #department_manage_table .folder").draggable({
    helper: "clone",
    opacity: .75,
    refreshPositions: true,
    revert: "invalid",
    revertDuration: 300,
    scroll: true
});
//
$("#department_manage_table .folder").each(function () {
    $(this).parents("#department_manage_table tr").droppable({
        accept: ".file, .folder",
        drop: function (e, ui) {
            var droppedEl = ui.draggable.parents("tr");
            $("#department_manage_table").treetable("move", droppedEl.data("ttId"), $(this).data("ttId"));
        },
        hoverClass: "accept",
        over: function (e, ui) {
            var droppedEl = ui.draggable.parents("tr");
            if (this != droppedEl[0] && !$(this).is(".expanded")) {
                $("#department_manage_table").treetable("expandNode", $(this).data("ttId"));
            }
        }
    });
});


function setMenuActivate(activate){

    var show_activate = 'Disable';
    if(activate == 'Enable'){
        show_activate = 'True';
    }

    var selected = $(".selected");
    if(selected.length){
        var department_id = $(".selected").data().ttId;
        bootbox.confirm("Are you sure?", function(result) {
           if(result){
               $.getJSON($SCRIPT_ROOT+'/department/department_activate',{
                   id: department_id,
                   activate: activate
               }, function(data){

                   bootbox.alert("Success");
                   if (!selected.data('ttParentId')) {
                        $("#department_manage_table tr").each(function (trindex, tritem) {
                            if($(tritem).data('ttParentId') == selected.data('ttId')){
                                $($(tritem).children()[1]).text(show_activate);
                            }
                        });
                   }else{
                       $(selected.children()[1]).text(show_activate);
                   }
               });
           }
        });
    }else{
        bootbox.alert("You must selected department");
    }
}
//
$("#department_manage_table tr").dblclick(function(){
    window.location.href = $SCRIPT_ROOT + '/department/edit_department/' + $(this).attr('data-tt-id');
});<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

"""定义测试的蓝图"""
from flask import Blueprint

qa = Blueprint('qa', __name__)
from . import views
from . import testCaseViews
from . import projectViews
<file_sep>{% extends "qa/qa_base.html" %}

{% block page_content %}
<!-- BEGIN CONTENT -->
<div class="row">
    <div class="col-md-12">
        <!-- BEGIN EXAMPLE TABLE PORTLET-->
        <div class="portlet box grey-cascade">
            <div class="portlet-title">
                <div class="caption">
                    {%if all_project %}
                    <i class="fa fa-globe"></i>所有项目
                    {% else %}
                    <i class="fa fa-globe"></i>我的项目
                    {% endif %}
                </div>
                <div class="actions">
                    <a href="{{ url_for('qa.all_project') }}" class="btn btn-primary btn-sm">所有项目</a>
                </div>
            </div>
            <div class="portlet-body">
                <table class="table table-striped table-bordered table-hover" id="project_table">
                <thead>
                <tr>
                    <th width="20px;">No</th>
                    <th>项目名称</th>
                    <th>JIRA_KEY</th>
                    <th>开发负责人</th>
                    <th>测试负责人</th>
                    <th>状态</th>
                    <th>最新版本</th>
                    <th>发布时间</th>
                    <th>项目用例</th>
                </tr>
                </thead>
                <tbody>
                    {% for project in projects %}
                    <tr data="{{ project.id }}">
                        <td>{{ loop.index }}</td>
                        <td>{{project.name }}</td>
                        <td>{{ project.project_key }}</td>
                        <td>{{ project.develop_leader.display_name }}</td>
                        <td>{{ project.test_leader.display_name }}</td>
                        <td>{{ project.status }}</td>
                        <td>{{ project.last_version_number or '' }}</td>
                        <td>{{ project.last_release_time or ''}}</td>
                        {% if not all_project %}
                            <td>
                                <a class="btn btn-xs green"
                                   href="{{ url_for('qa.project_test_case_page', project_id=project.id) }}">用例管理</a>
                            </td>
                        {% endif %}
                    </tr>
                    {% endfor %}
                </tbody>
                </table>
            </div>
        </div>
        <!-- END EXAMPLE TABLE PORTLET-->
    </div>
</div>
<!-- END PAGE CONTENT-->
{% endblock %}

{% block script %}
<script src="{{ url_for('static', filename='assets/global/plugins/bootbox/bootbox.min.js') }}"
        type="text/javascript"></script>
<script src="{{url_for('static', filename='js/qa/project_page.js')}}" type="text/javascript"></script>
<script type="text/javascript">
$SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
</script>
{% endblock %}

<file_sep>var ChartsAmcharts = function() {

    var init_jira_total_tasks_chart = function(total_tasks) {
        var data = [];
        for(index in total_tasks){
            data_dic = {};
            data_dic['status'] = total_tasks[index]['status'];
            data_dic['total_num'] = total_tasks[index]['total_num'];
            data.push(data_dic);
        }
        var chart = AmCharts.makeChart("jira_total_tasks_chart", {
            "type": "pie",
            "theme": "light",
            "fontFamily": 'Open Sans',
            "color":    '#888',
            "dataProvider": data,
            "valueField": "total_num",
            "titleField": "status",
            "balloonText": "[[title]]<br><span style='font-size:14px'><b>[[value]]</b> ([[percents]]%)</span>",
            "legend": {
                "alphaField": "alpha",
                "useGraphSettings": true,
                "valueAlign": "left",
                "valueWidth": 100
            },
            "export": {
                "enabled":true,
                "fileName":"total_tasks_chart",
                "libs":{
                    "path": Metronic.getGlobalPluginsPath() + "amcharts/amcharts/plugins/export/libs/"
                },
                "position":"bottom-right",
                "menu": [ {
                    "class": "export-main",
                    "menu": [ {
                      "label": "Download as image",
                      "menu": [ "PNG", "SVG" , "PDF"]
                    }, {
                      "label": "Save data",
                      "menu": [ "CSV", "XLSX" ]
                    }]
                 } ]
            }
        });

        $('#jira_total_tasks_chart').closest('.portlet').find('.fullscreen').click(function() {
            chart.invalidateSize();
        });
    }

    var init_jira_tasks_daily_update_chart = function(tasks_daily_update) {
        var data = [];
        for(index in tasks_daily_update){
            data_dic = {};
            data_dic['date'] = tasks_daily_update[index]['date'];
            data_dic['new_open'] = tasks_daily_update[index]['new_open'];
            data_dic['closed'] = tasks_daily_update[index]['closed'];
            data.push(data_dic);
        }
        var chart = AmCharts.makeChart("jira_tasks_daily_update_chart", {
            "type": "serial",
            "theme": "light",
            "fontFamily": 'Open Sans',
            "color":    '#888888',
            "pathToImages": Metronic.getGlobalPluginsPath() + "amcharts/amcharts/images/",
            "legend": {
                "equalWidths": false,
                "useGraphSettings": true,
                "valueAlign": "left",
                "valueWidth": 120
            },
            "dataProvider": data ,
            "valueAxes": [{
                "id": "distanceAxis",
                "axisAlpha": 0,
                "gridAlpha": 0,
                "position": "left"
//                "title": "distance"
            }, {
                "id": "new_openAxis",
                "axisAlpha": 0,
                "gridAlpha": 0,
                "labelsEnabled": false,
                "position": "right"
            }, {
//                "id": "durationAxis",
////                "duration": "mm",
////                "durationUnits": {
////                    "hh": "h ",
////                    "mm": "min"
////                },
//                "axisAlpha": 0,
//                "gridAlpha": 0,
//                "inside": true,
//                "position": "right",
//                "title": "duration"
            }],
            "graphs": [
//            {
//                "alphaField": "alpha",
//                "balloonText": "[[value]] miles",
//                "dashLengthField": "dashLength",
//                "fillAlphas": 0.7,
//                "legendPeriodValueText": "total: [[value.sum]] mi",
//                "legendValueText": "[[value]] mi",
//                "title": "当日任务总数",
//                "type": "column",
//                "valueField": "distance",
//                "valueAxis": "distanceAxis"
//            },
              {
                "balloonText": "新增任务数:[[value]]",
                "bullet": "round",
                "bulletBorderAlpha": 1,
                "useLineColorForBulletBorder": true,
                "bulletColor": "#FFFFFF",
                "bulletSizeField": "townSize",
                "dashLengthField": "dashLength",
                "descriptionField": "townName",
                "labelPosition": "right",
                "labelText": "[[townName2]]",
//                "legendValueText": "[[description]]/[[value]]",
                "legendValueText": "[[value]]",
                "title": "新增任务数",
                "fillAlphas": 0,
                "valueField": "new_open",
                "valueAxis": "distanceAxis"
            }, {
                "balloonText": "已关闭任务数:[[value]]",
                "bullet": "square",
                "bulletBorderAlpha": 1,
                "bulletBorderThickness": 1,
                "dashLengthField": "dashLength",
                "legendValueText": "[[value]]",
                "title": "已关闭任务数",
                "fillAlphas": 0,
                "valueField": "closed",
                "valueAxis": "distanceAxis"
            }],
            "chartScrollbar": {},
            "chartCursor": {
                "categoryBalloonDateFormat": "DD",
                "cursorAlpha": 0.1,
                "cursorColor": "#000000",
                "fullWidth": true,
                "valueBalloonsEnabled": false,
                "zoomable": false
            },
            "dataDateFormat": "YYYY-MM-DD",
            "categoryField": "date",
            "categoryAxis": {
                "dateFormats": [{
                    "period": "DD",
                    "format": "DD"
                }, {
//                    "period": "WW",
//                    "format": "MMM DD"
                }, {
                    "period": "MM",
                    "format": "MMM"
                }, {
                    "period": "YYYY",
                    "format": "YYYY"
                }],
                "parseDates": true,
                "autoGridCount": false,
                "axisColor": "#555555",
                "gridAlpha": 0.1,
                "gridColor": "#FFFFFF",
                "gridCount": 50
            },
            "export": {
                "enabled":true,
                "libs":{
                    "path": Metronic.getGlobalPluginsPath() + "amcharts/amcharts/plugins/export/libs/"
                },
                "position":"bottom-right",
                "forceRemoveImages":true,
                "fileName":"tasks_daily_update_chart",
                "menu": [ {
                    "class": "export-main",
                    "menu": [ {
                      "label": "Download as image",
                      "menu": [ "PNG", "SVG" , "PDF"]
                    }, {
                      "label": "Save data",
                      "menu": [ "CSV", "XLSX" ]
                    } ]
                 } ]
            }
        });

        $('#jira_tasks_daily_update_chart').closest('.portlet').find('.fullscreen').click(function() {
            chart.invalidateSize();
        });
    }

    var init_jira_tasks_daily_summary_chart = function(tasks_daily_summary) {  //tasks_daily_update
        var data = [];
        for(index in tasks_daily_summary){
            data_dic = {};
            data_dic['date'] = tasks_daily_summary[index]['date'];
//            if(index == 0){
                data_dic['open_summary'] = tasks_daily_summary[index]['open_summary'];
                data_dic['closed_summary'] = tasks_daily_summary[index]['closed_summary'];
//            }else{
//                tasks_daily_update[index]['new_open'] = tasks_daily_update[index]['new_open'] + tasks_daily_update[index-1]['new_open'];
//                data_dic['open_summary'] = tasks_daily_update[index]['new_open'];
//                tasks_daily_update[index]['closed'] = tasks_daily_update[index]['closed'] + tasks_daily_update[index-1]['closed'];
//                data_dic['closed_summary'] = tasks_daily_update[index]['closed'];
//            }
            data.push(data_dic);
        }
        var chart = AmCharts.makeChart("jira_tasks_daily_summary_chart", {
            "type": "serial",
            "theme": "light",
            "fontFamily": 'Open Sans',
            "color":    '#888888',
            "pathToImages": Metronic.getGlobalPluginsPath() + "amcharts/amcharts/images/",
            "legend": {
                "equalWidths": false,
                "useGraphSettings": true,
                "valueAlign": "left",
                "valueWidth": 120
            },
            "dataProvider": data ,
            "valueAxes": [{
                "id": "distanceAxis",
                "axisAlpha": 0,
                "gridAlpha": 0,
                "position": "left"
            }],
            "graphs": [
              {
                "balloonText": "新增任务汇总:[[value]]",
                "bullet": "round",
                "bulletBorderAlpha": 1,
                "useLineColorForBulletBorder": true,
                "bulletColor": "#FFFFFF",
                "dashLengthField": "dashLength",
                "labelPosition": "right",
                "legendValueText": "[[value]]",
                "title": "新增任务汇总",
                "fillAlphas": 0,
                "valueField": "open_summary",
                "valueAxis": "distanceAxis"
            }, {
                "balloonText": "关闭任务汇总:[[value]]",
                "bullet": "square",
                "bulletBorderAlpha": 1,
                "bulletBorderThickness": 1,
                "dashLengthField": "dashLength",
                "legendValueText": "[[value]]",
                "title": "关闭任务汇总",
                "fillAlphas": 0,
                "valueField": "closed_summary",
                "valueAxis": "distanceAxis"
            }],
            "chartScrollbar": {},
            "chartCursor": {
                "categoryBalloonDateFormat": "DD",
                "cursorAlpha": 0.1,
                "cursorColor": "#000000",
                "fullWidth": true,
                "valueBalloonsEnabled": false,
                "zoomable": false
            },
            "dataDateFormat": "YYYY-MM-DD",
            "categoryField": "date",
            "categoryAxis": {
                "dateFormats": [{
                    "period": "DD",
                    "format": "DD"
                }, {
//                    "period": "WW",
//                    "format": "MMM DD"
                }, {
                    "period": "MM",
                    "format": "MMM"
                }, {
                    "period": "YYYY",
                    "format": "YYYY"
                }],
                "parseDates": true,
                "autoGridCount": false,
                "axisColor": "#555555",
                "gridAlpha": 0.1,
                "gridColor": "#FFFFFF",
                "gridCount": 50
            },
            "export": {
                "enabled":true,
                "libs":{
                    "path": Metronic.getGlobalPluginsPath() + "amcharts/amcharts/plugins/export/libs/"
                },
                "position":"bottom-right",
                "fileName":"jira_tasks_daily_summary_chart",
                "menu": [ {
                    "class": "export-main",
                    "menu": [ {
                      "label": "Download as image",
                      "menu": [ "PNG", "SVG" , "PDF"]
                    }, {
                      "label": "Save data",
                      "menu": [ "CSV", "XLSX" ]
                    } ]
                 } ]
            }
        });

        $('#jira_tasks_daily_summary_chart').closest('.portlet').find('.fullscreen').click(function() {
            chart.invalidateSize();
        });
    }

    var init_jira_total_tasks_developer_open_chart = function(total_tasks_developer_open) {
        var data = [];
        for(index in total_tasks_developer_open){
            data_dic = {};
            data_dic['assigner'] = total_tasks_developer_open[index]['assigner'];
            data_dic['assignerNum'] = total_tasks_developer_open[index]['total_tasks'];
            data.push(data_dic);
        }
        var chart = AmCharts.makeChart("jira_total_tasks_developer_open_chart", {
            "type": "serial",
            "theme": "light",
            "fontFamily": 'Open Sans',
            "color":    '#888',
            "legend": {
                "equalWidths": false,
                "useGraphSettings": true,
                "valueAlign": "left",
                "valueWidth": 120
            },
            "dataProvider": data,
            "pathToImages": Metronic.getGlobalPluginsPath() + "amcharts/amcharts/images/",
            "valueAxes": [{
                "id": "assignerNumAxis",
                "axisAlpha": 0,
                "gridAlpha": 0,
                "position": "left"
            }],
            "graphs": [{
                "alphaField": "alpha",
                "balloonText": "[[value]]",
                "dashLengthField": "dashLength",
                "fillAlphas": 0.7,
                "legendPeriodValueText": "total: [[value.sum]]",
                "legendValueText": "[[value]]",
                "title": "开发人员进行中任务数:",
                "type": "column",
                "valueField": "assignerNum",
                "valueAxis": "assignerNumAxis"
            }],
            "chartScrollbar": {},
            "chartCursor": {
                "cursorAlpha": 0.1,
                "cursorColor": "#000000",
                "fullWidth": true,
                "valueBalloonsEnabled": false,
                "zoomable": false
            },
            "categoryField": "assigner",
            "categoryAxis": {
                "autoGridCount": false,
                "axisColor": "#555555",
                "gridAlpha": 0.1,
                "gridColor": "#FFFFFF",
                "gridCount": 50
            },
            "export": {
                "enabled":true,
                "fileName":"total_tasks_developer_open_chart",
                "libs":{
                    "path": Metronic.getGlobalPluginsPath() + "amcharts/amcharts/plugins/export/libs/"
                },
                "position":"bottom-right",
                "menu": [ {
                    "class": "export-main",
                    "menu": [ {
                      "label": "Download as image",
                      "menu": [ "PNG", "SVG" , "PDF"]
                    }, {
                      "label": "Save data",
                      "menu": [ "CSV", "XLSX" ]
                    } ]
                 } ]
            }
        });

        $('#jira_total_tasks_developer_open_chart').closest('.portlet').find('.fullscreen').click(function() {
            chart.invalidateSize();
        });
    }

    var init_jira_total_tasks_each_person_chart = function(total_tasks_each_person) {
        var data = [];
        for(index in total_tasks_each_person){
            data_dic = {};
            data_dic['assigner'] = total_tasks_each_person[index]['assigner'];
            data_dic['total_tasks'] = total_tasks_each_person[index]['total_tasks'];
            data.push(data_dic);
        }
        var chart = AmCharts.makeChart("jira_total_tasks_each_person_chart", {
            "type": "pie",
            "theme": "light",
            "fontFamily": 'Open Sans',
            "color":    '#888',
            "dataProvider":data,
            "valueField": "total_tasks",
            "titleField": "assigner",
//            "outlineAlpha": 0.4,
//            "depth3D": 15,
            "balloonText": "[[title]]<br><span style='font-size:14px'><b>[[value]]</b> ([[percents]]%)</span>",
//            "angle": 30,
            "export": {
                "enabled":true,
                "fileName":"total_tasks_each_person_chart",
                "libs":{
                    "path": Metronic.getGlobalPluginsPath() + "amcharts/amcharts/plugins/export/libs/"
                },
                "position":"bottom-right",
                "menu": [ {
                    "class": "export-main",
                    "menu": [ {
                      "label": "Download as image",
                      "menu": [ "PNG", "SVG" , "PDF"]
                    }, {
                      "label": "Save data",
                      "menu": [ "CSV", "XLSX" ]
                    } ]
                 } ]
            }
        });

        jQuery('.jira_total_tasks_each_person_chart_input').off().on('input change', function() {
            var property = jQuery(this).data('property');
            var target = chart;
            var value = Number(this.value);
            chart.startDuration = 0;
            if (property == 'innerRadius') {
                value += "%";
            }
            target[property] = value;
            chart.validateNow();
        });

        $('#jira_total_tasks_each_person_chart').closest('.portlet').find('.fullscreen').click(function() {
            chart.invalidateSize();
        });
    }

    var init_jira_total_tasks_tester_open_chart = function(total_tasks_tester_open) {
        var data = [];
        for(index in total_tasks_tester_open){
            data_dic = {};
            data_dic['creator'] = total_tasks_tester_open[index]['creator'];
            data_dic['creatorNum'] = total_tasks_tester_open[index]['creatorNum'];
            data.push(data_dic);
        }
        var chart = AmCharts.makeChart("jira_total_tasks_tester_open_chart", {
            "type": "serial",
            "theme": "light",
            "fontFamily": 'Open Sans',
            "pathToImages": Metronic.getGlobalPluginsPath() + "amcharts/amcharts/images/",
            "color":    '#888888',
            "legend": {
                "equalWidths": false,
                "useGraphSettings": true,
                "valueAlign": "left",
                "valueWidth": 120
            },
            "dataProvider": data,
            "valueAxes": [{
                "id": "creatorNumAxis",
                "axisAlpha": 0,
                "gridAlpha": 0,
                "position": "left"
            }],
            "graphs": [{
                "alphaField": "alpha",
                "balloonText": "[[value]]",
                "dashLengthField": "dashLength",
                "fillAlphas": 0.7,
                "legendPeriodValueText": "total: [[value.sum]]",
                "legendValueText": "[[value]]",
                "title": "测试人员提交任务统计:",
                "type": "column",
                "valueField": "creatorNum",
                "valueAxis": "creatorNumAxis"
            }],
            "chartScrollbar": {},
            "chartCursor": {
                "cursorAlpha": 0.1,
                "cursorColor": "#000000",
                "fullWidth": true,
                "valueBalloonsEnabled": false,
                "zoomable": false
            },
            "categoryField": "creator",
            "categoryAxis": {
                "autoGridCount": false,
                "axisColor": "#555555",
                "gridAlpha": 0.1,
                "gridColor": "#FFFFFF",
                "gridCount": 50
            },
            "export": {
                "enabled":true,
                "position":"bottom-right",
                "fileName":"jira_total_tasks_tester_open_chart",
                "libs":{
                    "path": Metronic.getGlobalPluginsPath() + "amcharts/amcharts/plugins/export/libs/"
                },
                "position":"bottom-right",
                "menu": [ {
                    "class": "export-main",
                    "menu": [ {
                      "label": "Download as image",
                      "menu": [ "PNG", "SVG" , "PDF"]
                    }, {
                      "label": "Save data",
                      "menu": [ "CSV", "XLSX" ]
                    }]
                 } ]
            }
        });

        $('#jira_total_tasks_tester_open_chart').closest('.portlet').find('.fullscreen').click(function() {
            chart.invalidateSize();
        });
    }

    var init_jira_daily_tasks_tester_open_chart = function(daily_tasks_tester_open) {
        var data = [];
        for(index in daily_tasks_tester_open){
            data_dic = {};
            data_dic['creator'] = daily_tasks_tester_open[index]['creator'];
            data_dic['creatorNum'] = daily_tasks_tester_open[index]['creatorNum'];
            data.push(data_dic);
        }
        var chart = AmCharts.makeChart("jira_daily_tasks_tester_open_chart", {
            "type": "serial",
            "theme": "light",
            "fontFamily": 'Open Sans',
            "pathToImages": Metronic.getGlobalPluginsPath() + "amcharts/amcharts/images/",
            "color":    '#888888',
            "legend": {
                "equalWidths": false,
                "useGraphSettings": true,
                "valueAlign": "left",
                "valueWidth": 120
            },
            "dataProvider": data,
            "valueAxes": [{
                "id": "creatorNumAxis",
                "axisAlpha": 0,
                "gridAlpha": 0,
                "position": "left"
            }],
            "graphs": [{
                "alphaField": "alpha",
                "balloonText": "[[value]]",
                "dashLengthField": "dashLength",
                "fillAlphas": 0.7,
                "legendPeriodValueText": "total: [[value.sum]]",
                "legendValueText": "[[value]]",
                "title": "测试人员每日提交任务数:",
                "type": "column",
                "valueField": "creatorNum",
                "valueAxis": "creatorNumAxis"
            }],
            "chartScrollbar": {},
            "chartCursor": {
                "cursorAlpha": 0.1,
                "cursorColor": "#000000",
                "fullWidth": true,
                "valueBalloonsEnabled": false,
                "zoomable": false
            },
            "categoryField": "creator",
            "categoryAxis": {
                "autoGridCount": false,
                "axisColor": "#555555",
                "gridAlpha": 0.1,
                "gridColor": "#FFFFFF",
                "gridCount": 50
            },
            "export": {
                "enabled":true,
                "fileName":"jira_daily_tasks_tester_open_chart",
                "libs":{
                    "path": Metronic.getGlobalPluginsPath() + "amcharts/amcharts/plugins/export/libs/"
                },
                "position":"bottom-right",
                "menu": [ {
                    "class": "export-main",
                    "menu": [ {
                      "label": "Download as image",
                      "menu": [ "PNG", "SVG" , "PDF"]
                    }, {
                      "label": "Save data",
                      "menu": [ "CSV", "XLSX" ]
                    }]
                 } ]
            }
        });

        $('#jira_daily_tasks_tester_open_chart').closest('.portlet').find('.fullscreen').click(function() {
            chart.invalidateSize();
        });
    }

    var init_jira_total_tasks_developer_closed_chart = function(total_tasks_developer_closed) {
        var data = [];
        for(index in total_tasks_developer_closed){
            data_dic = {};
            data_dic['assigner'] = total_tasks_developer_closed[index]['assigner'];
            data_dic['assignerNum'] = total_tasks_developer_closed[index]['assignerNum'];
            data.push(data_dic);
        }
        var chart = AmCharts.makeChart("jira_total_tasks_developer_closed_chart", {
            "type": "serial",
            "theme": "light",
            "fontFamily": 'Open Sans',
            "pathToImages": Metronic.getGlobalPluginsPath() + "amcharts/amcharts/images/",
            "color":    '#888888',
            "legend": {
                "equalWidths": false,
                "useGraphSettings": true,
                "valueAlign": "left",
                "valueWidth": 120
            },
            "dataProvider": data,
            "valueAxes": [{
                "id": "assignerNumAxis",
                "axisAlpha": 0,
                "gridAlpha": 0,
                "position": "left"
            }],
            "graphs": [{
                "alphaField": "alpha",
                "balloonText": "[[value]]",
                "dashLengthField": "dashLength",
                "fillAlphas": 0.7,
                "legendPeriodValueText": "total: [[value.sum]]",
                "legendValueText": "[[value]]",
                "title": "开发人员关闭任务统计:",
                "type": "column",
                "valueField": "assignerNum",
                "valueAxis": "assignerNumAxis"
            }],
            "chartScrollbar": {},
            "chartCursor": {
                "cursorAlpha": 0.1,
                "cursorColor": "#000000",
                "fullWidth": true,
                "valueBalloonsEnabled": false,
                "zoomable": false
            },
            "categoryField": "assigner",
            "categoryAxis": {
                "autoGridCount": false,
                "axisColor": "#555555",
                "gridAlpha": 0.1,
                "gridColor": "#FFFFFF",
                "gridCount": 50
            },
            "export": {
                "enabled":true,
                "fileName":"jira_total_tasks_developer_closed_chart",
                "libs":{
                    "path": Metronic.getGlobalPluginsPath() + "amcharts/amcharts/plugins/export/libs/"
                },
                "position":"bottom-right",
                "menu": [ {
                    "class": "export-main",
                    "menu": [ {
                      "label": "Download as image",
                      "menu": [ "PNG", "SVG" , "PDF"]
                    }, {
                      "label": "Save data",
                      "menu": [ "CSV", "XLSX" ]
                    }]
                 } ]
            }
        });

        $('#jira_total_tasks_developer_closed_chart').closest('.portlet').find('.fullscreen').click(function() {
            chart.invalidateSize();
        });
    }

    var init_jira_daily_tasks_developer_closed_chart = function(daily_tasks_developer_closed) {
        var data = [];
        for(index in daily_tasks_developer_closed){
            data_dic = {};
            data_dic['assigner'] = daily_tasks_developer_closed[index]['assigner'];
            data_dic['assignerNum'] = daily_tasks_developer_closed[index]['assignerNum'];
            data.push(data_dic);
        }
        var chart = AmCharts.makeChart("jira_daily_tasks_developer_closed_chart", {
            "type": "serial",
            "theme": "light",
            "fontFamily": 'Open Sans',
            "pathToImages": Metronic.getGlobalPluginsPath() + "amcharts/amcharts/images/",
            "color":    '#888888',
            "legend": {
                "equalWidths": false,
                "useGraphSettings": true,
                "valueAlign": "left",
                "valueWidth": 120
            },
            "dataProvider": data,
            "valueAxes": [{
                "id": "assignerNumAxis",
                "axisAlpha": 0,
                "gridAlpha": 0,
                "position": "left"
            }],
            "graphs": [{
                "alphaField": "alpha",
                "balloonText": "[[value]]",
                "dashLengthField": "dashLength",
                "fillAlphas": 0.7,
                "legendPeriodValueText": "total: [[value.sum]]",
                "legendValueText": "[[value]]",
                "title": "开发人员每日关闭任务数:",
                "type": "column",
                "valueField": "assignerNum",
                "valueAxis": "assignerNumAxis"
            }],
            "chartScrollbar": {},
            "chartCursor": {
                "cursorAlpha": 0.1,
                "cursorColor": "#000000",
                "fullWidth": true,
                "valueBalloonsEnabled": false,
                "zoomable": false
            },
            "categoryField": "assigner",
            "categoryAxis": {
                "autoGridCount": false,
                "axisColor": "#555555",
                "gridAlpha": 0.1,
                "gridColor": "#FFFFFF",
                "gridCount": 50
            },
            "export": {
                "enabled":true,
                "fileName":"jira_daily_tasks_developer_closed_chart",
                "libs":{
                    "path": Metronic.getGlobalPluginsPath() + "amcharts/amcharts/plugins/export/libs/"
                },
                "position":"bottom-right",
                "menu": [ {
                    "class": "export-main",
                    "menu": [ {
                      "label": "Download as image",
                      "menu": [ "PNG", "SVG" , "PDF"]
                    }, {
                      "label": "Save data",
                      "menu": [ "CSV", "XLSX" ]
                    }]
                 } ]
            }
        });

        $('#jira_daily_tasks_developer_closed_chart').closest('.portlet').find('.fullscreen').click(function() {
            chart.invalidateSize();
        });
    }

    var init_jira_total_tasks_by_module_chart = function(total_tasks_by_module) {
        var data = [];
        for(index in total_tasks_by_module){
            data_dic = {};
            data_dic['module_name'] = total_tasks_by_module[index]['module_name'];
            data_dic['total_num'] = total_tasks_by_module[index]['total_num'];
            data_dic['open_num'] = total_tasks_by_module[index]['open_num'];
            data.push(data_dic);
        }
        var chart = AmCharts.makeChart("jira_total_tasks_by_module_chart", {
            "type": "serial",
            "theme": "light",
            "fontFamily": 'Open Sans',
            "pathToImages": Metronic.getGlobalPluginsPath() + "amcharts/amcharts/images/",
            "color":    '#888888',
            "legend": {
                "equalWidths": false,
                "useGraphSettings": true,
                "valueAlign": "left",
                "valueWidth": 120
            },
            "dataProvider":data,
            "valueAxes": [{
                "axisAlpha": 0,
                "gridAlpha": 0,
                "position": "left"
            }],
            "graphs": [{
                "balloonText": "<b>[[value]]</b>",
                "fillAlphas": 0.9,
                "lineAlpha": 0.2,
                "legendPeriodValueText": "total: [[value.sum]]",
                "title": "该模块总数",
                "type": "column",
                "valueField": "total_num"
            }, {
                "balloonText": "<b>[[value]]</b>",
                "fillAlphas": 0.9,
                "lineAlpha": 0.2,
                "legendPeriodValueText": "total: [[value.sum]]",
                "title": "进行中",
                "type": "column",
//                "clustered":false,
//                "columnWidth":0.5,
                "valueField": "open_num"
            }],
            "chartScrollbar": {},
            "chartCursor": {
                "cursorAlpha": 0.1,
                "cursorColor": "#000000",
                "fullWidth": true,
                "valueBalloonsEnabled": false,
                "zoomable": false
            },
            "categoryField": "module_name",
            "categoryAxis": {
                "autoGridCount": false,
                "axisColor": "#555555",
                "gridAlpha": 0.1,
                "gridColor": "#FFFFFF",
                "gridCount": 50
            },
            "export": {
                "enabled":true,
                "fileName":"total_tasks_by_module_chart",
                "libs":{
                    "path": Metronic.getGlobalPluginsPath() + "amcharts/amcharts/plugins/export/libs/"
                },
                "position":"bottom-right",
                "menu": [ {
                    "class": "export-main",
                    "menu": [ {
                      "label": "Download as image",
                      "menu": [ "PNG", "SVG" , "PDF"]
                    }, {
                      "label": "Save data",
                      "menu": [ "CSV", "XLSX" ]
                    }]
                 } ]
            }
        });
        $('#jira_total_tasks_by_module_chart').closest('.portlet').find('.fullscreen').click(function() {
            chart.invalidateSize();
        });
    }

    return {
        //main function to initiate the module

        init: function(jira_key,date,chart) {

            if(date){
                var el = $(chart).parent(".portlet-body");
                Metronic.blockUI({
                    target: el,
                    animate: true,
                    overlayColor: 'none'
                });
                $.ajax({
                    type: "GET",
                    data:{jira_key:jira_key,date:date},
                    url: "/project/android_project_jira",
                    success: function(jira_data) {
                        Metronic.unblockUI(el);
                        var jira_datas = JSON.parse(jira_data.replace(/&#34;/g,"\""));
                        if(chart.attr('id') == 'jira_daily_tasks_developer_closed_chart'){
                            console.log(date + ' daily_tasks_developer_closed:');
                            console.log(jira_datas['daily_tasks_developer_closed']);
                            init_jira_daily_tasks_developer_closed_chart(jira_datas['daily_tasks_developer_closed']);
                        }if(chart.attr('id') == 'jira_daily_tasks_tester_open_chart'){
                            console.log(date + ' daily_tasks_tester_open:');
                            console.log(jira_datas['daily_tasks_tester_open']);
                            init_jira_daily_tasks_tester_open_chart(jira_datas['daily_tasks_tester_open']);
                        }
                    },
                    error: function() {
                        Metronic.unblockUI(el);
                    }
                });
            }else{
                showMask();
                $.ajax({
                    type: "GET",
                    data:{jira_key:jira_key},
                    url: "/project/android_project_jira",
                    success: function(jira_data) {
                        unshowMask();
                        var jira_datas = JSON.parse(jira_data.replace(/&#34;/g,"\""));
                        console.log(jira_datas);
                        init_jira_total_tasks_chart(jira_datas['total_tasks']);
    //                    init_jira_tasks_daily_update_chart(jira_datas['tasks_daily_update']);
    //                    init_jira_tasks_daily_summary_chart(jira_datas['tasks_daily_update']);
                        init_jira_tasks_daily_summary_chart(jira_datas['tasks_daily_summary']);
    //                    init_jira_total_tasks_each_person_chart(jira_datas['total_tasks_each_person']);
                        init_jira_total_tasks_developer_open_chart(jira_datas['total_tasks_developer_open']);
                        init_jira_total_tasks_tester_open_chart(jira_datas['total_tasks_tester_open']);
                        init_jira_total_tasks_developer_closed_chart(jira_datas['total_tasks_developer_closed']);
                        init_jira_daily_tasks_tester_open_chart(jira_datas['daily_tasks_tester_open']);
                        init_jira_daily_tasks_developer_closed_chart(jira_datas['daily_tasks_developer_closed']);
                        init_jira_total_tasks_by_module_chart(jira_datas['total_tasks_by_module']);
                    },
                    error: function() {
                        bootbox.alert('Error on reloading the content. Please check your connection and try again.');
                        unshowMask();
                    }
                });
            }

        }

    };

}();<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

"""系统主入口"""
from flask import Blueprint

main = Blueprint('main', __name__)
from . import views
<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""
from flask.ext.wtf import Form
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import Required, Length, Email, Regexp, EqualTo
from wtforms import ValidationError
from models import User


class LoginForm(Form):

    email = StringField('Email', validators=[Required(), Length(1, 64), Email()])
    password = PasswordField('<PASSWORD>', validators=[Required()])
    remember_me = BooleanField('Remember me', default=False)
    submit = SubmitField('Sign In')


class ChangePasswordForm(Form):

    old_password = PasswordField('Old Password', validators=[Required()])
    password = PasswordField('New Password', validators=[Required(), EqualTo('password2',
                                                         message='Passwords must match')])
    password2 = PasswordField('Confirm password', validators=[Required()])


class ChangeEmailForm(Form):
    email = StringField('New Email', validators=[Required(), Length(1, 64), Email()])

    def validate_email(self, field):
        if User.query.filter_by(email=field.data).first():
            raise ValidationError('Email already registered.')


class AdministratorForm(Form):
    """管理员账号编辑"""

    email = StringField('Email', validators=[Required(), Length(1, 64), Email()])
    password = PasswordField('<PASSWORD>', validators=[Required()])
    username = StringField('Username', validators=[Required(), Length(1, 64)])
    submit = SubmitField('Submit')<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

"""定义auth模块的蓝图"""
from flask import Blueprint

auth = Blueprint('auth', __name__)
from . import views
from . import systemView<file_sep># -*- coding: utf-8 -*-
# Created by changjie.fan on 2016/7/5
"""
工程项目代码views
"""
import json
import os

from flask import render_template, redirect, url_for, request, flash, current_app, session, send_file
from flask.ext.login import login_user, logout_user, login_required, current_user
from flask.ext.mail import Mail, Message
from functools import update_wrapper

from . import project
from tms import db, mail
from androidProjectModules import *
from tms.utils.excelOption import ExcelOptions


@project.route('/project_add_manifest/<int:project_id>', methods=['GET', 'POST'])
@login_required
def project_add_manifest(project_id):
    """新增项目分支货单"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '新增项目分支货单' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:
        session['breadcrumbs'].append({'text': '新增项目分支货单', 'url': url_for('project.project_add_manifest', project_id=project_id)})

    project_obj = ProjectAndroid.query.filter(ProjectAndroid.id == project_id).first()
    project_code = ProjectCode()
    if request.method == 'POST':
        manifest_url = request.form.get('manifest_url', '').strip()
        manifest_branch = request.form.get('manifest_branch', '').strip()
        manifest_name = request.form.get('manifest_name', '').strip()
        manifest_repo = request.form.get('manifest_repo', '').strip()
        branch_name = request.form.get('branch_name', '').strip()
        # local_repo_path = request.form.get('local_repo_path', '').strip()
        platform_name = request.form.get('platform_name', '').strip()
        commit_notice_email = request.form.get('commit_notice_email', '').strip()
        default_project_code = request.form.get('default_project_code', 'off').strip()

        # 同一个项目不能添加重复的分支货单
        project_code = ProjectCode.query.filter(ProjectCode.manifest_url == manifest_url).first()
        if project_code:
            project_code.project_android.append(project_obj)
            if default_project_code == 'on':
                project_obj.default_project_code = project_code.id
                db.session.add(project_obj)
            db.session.add(project_code)
            db.session.commit()
        else:
            project_code = ProjectCode()
            project_code.manifest_url = manifest_url
            project_code.manifest_branch = manifest_branch
            project_code.manifest_name = manifest_name
            project_code.manifest_repo = manifest_repo
            # project_code.local_repo_path = local_repo_path
            project_code.branch_name = branch_name
            project_code.platform_name = platform_name

            # 项目配置的加上分支配置的邮件地址
            if project_obj.commit_notice_email:
                commit_notice_email_list = list(set([tmp.strip() for tmp in commit_notice_email.split(';') if tmp.strip()] + [tmp for tmp in project_obj.commit_notice_email.split(';') if tmp.strip()]))
            else:
                commit_notice_email_list = list(set([tmp.strip() for tmp in commit_notice_email.split(';') if tmp.strip()]))
            project_code.commit_notice_email = ';'.join(commit_notice_email_list)
            project_code.activate = True
            project_code.project_id = project_obj.id
            project_code.project_android.append(project_obj)
            db.session.add(project_code)
            db.session.commit()
            if default_project_code == 'on':
                project_obj.default_project_code = project_code.id
                db.session.add(project_obj)
                db.session.commit()

            flash(u'新增成功!', 'info')
            return redirect(url_for('project.edit_android_project_page', project_id=project_id))

    return render_template('project/android/project_add_manifest.html',
                           project_code=project_code,
                           project=project_obj)


@project.route('/project_edit_manifest/<int:project_android_id>/<int:project_code_id>', methods=['GET', 'POST'])
@login_required
def project_edit_manifest(project_android_id, project_code_id):
    """
    修改项目分支货单
    :param project_code_id:
    :return:
    """

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '编辑项目分支货单' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index + 1]
            break
    else:
        session['breadcrumbs'].append(
            {'text': '编辑项目分支货单', 'url': url_for('project.project_edit_manifest',
                                                project_android_id=project_android_id,
                                                project_code_id=project_code_id)})

    project_obj = ProjectAndroid.query.filter(ProjectAndroid.id == project_android_id).first()
    project_code = ProjectCode.query.filter(ProjectCode.id == project_code_id).first()

    if request.method == 'POST':
        manifest_url = request.form.get('manifest_url', '').strip()
        manifest_branch = request.form.get('manifest_branch', '').strip()
        manifest_name = request.form.get('manifest_name', '').strip()
        manifest_repo = request.form.get('manifest_repo', '').strip()
        branch_name = request.form.get('branch_name', '').strip()
        # local_repo_path = request.form.get('local_repo_path', '').strip()
        platform_name = request.form.get('platform_name', '').strip()
        commit_notice_email = request.form.get('commit_notice_email', '').strip()
        default_project_code = request.form.get('default_project_code', 'off').strip()

        # 同一个项目不能添加重复的分支货单
        other_project_code = ProjectCode.query.filter(ProjectCode.manifest_url == manifest_url).first()
        if other_project_code and other_project_code.id != project_code_id:
            flash(u'相同货单已存在!', 'warning')
        else:
            project_code.manifest_url = manifest_url
            project_code.manifest_branch = manifest_branch
            project_code.manifest_name = manifest_name
            project_code.manifest_repo = manifest_repo
            # project_code.local_repo_path = local_repo_path
            project_code.branch_name = branch_name
            project_code.platform_name = platform_name

            commit_notice_email_list = list(set([tmp.strip() for tmp in commit_notice_email.split(';') if tmp.strip()]))
            # 项目配置的加上分支配置的邮件地址

            for project_tmp in project_code.project_android:
                current_app.logger.info(project_tmp.commit_notice_email)
                if project_tmp.commit_notice_email:
                    if commit_notice_email_list:
                        for tmp in project_tmp.commit_notice_email.split(';'):
                            if tmp.strip():
                                commit_notice_email_list.append(tmp)

            project_code.commit_notice_email = ';'.join( list(set(commit_notice_email_list)))
            project_code.activate = True

            if default_project_code == 'on':
                project_obj.default_project_code = project_code.id
                db.session.add(project_obj)
            elif default_project_code == 'off':
                project_obj.default_project_code = 0
                db.session.add(project_obj)

            db.session.add(project_code)
            db.session.commit()

            flash(u'修改成功!', 'info')
            return redirect(url_for('project.project_edit_manifest',
                                    project_android_id=project_android_id,
                                    project_code_id=project_code_id))

    return render_template('project/android/project_add_manifest.html',
                           project_code=project_code,
                           project=project_obj)


@project.route('/project_code_history/<int:project_id>', defaults={'project_code_id': None}, methods=['GET', 'POST'])
@project.route('/project_code_history/<int:project_id>/<int:project_code_id>', methods=['GET', 'POST'])
@login_required
def project_code_history(project_id, project_code_id):
    """
    项目项目指定分支上的所有提交记录，默认显示项目的默认分支
    :param project_id:
    :return:
    """

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '项目历史' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index + 1]
            break
    else:
        session['breadcrumbs'].append(
            {'text': '项目历史', 'url': url_for('project.project_code_history', project_id=project_id, project_code_id=project_code_id)})

    project_obj = ProjectAndroid.query.filter(ProjectAndroid.id == project_id).first()
    if not project_code_id:
        project_code_id = project_obj.default_project_code

    # 项目所有的提货单
    project_code_list = project_obj.project_code

    return render_template('project/android/project_code_history.html',
                           project=project_obj,
                           project_code_id=project_code_id,
                           project_code_list=project_code_list)


@project.route('/project_code_jira_data')
@login_required
def project_code_jira_data():
    """
    项目提交记录的数据
    :param project_code_id:
    :return:
    """

    start = int(request.args.get('start', 0))
    length = int(request.args.get('length', 10))
    search_type = request.args.get('search_type')
    search_text = request.args.get('search_text', '').strip().replace('%', '')
    project_code_id = request.args.get('project_code_id', '')

    search_sql = "SELECT id, task_key, summary, description, category FROM project_branch_task WHERE " \
                 "project_code_id={0} ".format(project_code_id)

    if search_type and search_text:
        search_sql = search_sql + " AND {0} like '%{1}%'".format(search_type, search_text)
    else:
        search_sql = search_sql.format(project_code_id)

    search_sql += ' ORDER BY id DESC'
    jira_tasks = db.session.execute(search_sql).fetchall()

    result = []
    index = start + 1
    for task in jira_tasks[start:start+length]:
        record = {}
        record['index'] = index
        record['id'] = task[0]
        record['task_key'] = task[1]
        record['summary'] = task[2]
        record['description'] = task[3]
        record['category'] = task[4]
        result.append(record)
        index += 1

    return '{"recordsTotal": %s ,"recordsFiltered": %s,"data":%s}' % (len(jira_tasks), len(jira_tasks), json.dumps(result))


@project.route('/project_code_commit_data')
@login_required
def project_code_commit_data():
    """
    项目提交记录的数据
    :param project_code_id:
    :return:
    """

    start = int(request.args.get('start', 0))
    length = int(request.args.get('length', 10))
    search_type = request.args.get('search_type')
    search_text = request.args.get('search_text', '').strip().replace('%', '')
    project_code_id = request.args.get('project_code_id', '')

    search_sql = "SELECT id, repo_path, auth_name, auth_email, auth_date, commit_name, commit_email, commit_date, " \
                 "message, change_file, commit_id FROM project_branch_commit WHERE project_code_id={0} ".format(project_code_id)

    if search_type and search_text:
        if search_type == 'commit_date':
            if not (search_text.startswith('=') or search_text.startswith('>=') or search_text.startswith('>') or
                        search_text.startswith('=<') or search_text.startswith('<')):
                search_sql = search_sql + " AND {0} like '%{1}%'".format(search_type, search_text)
            else:
                search_sql = search_sql + " AND {0} {1}".format(search_type, search_text)
        else:
            search_sql = search_sql + " AND {0} like '%{1}%'".format(search_type, search_text)
    else:
        search_sql = search_sql.format(project_code_id)

    search_sql += ' ORDER BY id DESC'

    commits = db.session.execute(search_sql).fetchall()

    result = []
    index = start + 1
    for commit in commits[start:start+length]:
        record = {}

        record['index'] = index
        record['id'] = commit[0]
        record['repo_path'] = commit[1]
        record['auth_name'] = commit[2]
        record['auth_email'] = commit[3]
        record['auth_date'] = commit[4]
        record['commit_name'] = commit[5]
        record['commit_email'] = commit[6]
        record['commit_date'] = commit[7]
        record['message'] = commit[8].replace('<', '&lt;')
        record['change_file'] = commit[9]
        record['commit_id'] = commit[10][:8]
        result.append(record)
        index += 1

    return '{"recordsTotal": %s ,"recordsFiltered": %s,"data":%s}' % (len(commits), len(commits), json.dumps(result))


@project.route('/project_branch_commit_detail/<int:project_branch_commit_id>')
@login_required
def project_branch_commit_detail(project_branch_commit_id):
    """
    显示commit的详细信息
    :param project_branch_commit_id:
    :return:
    """

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '提交详细信息' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index + 1]
            break
    else:
        session['breadcrumbs'].append(
            {'text': '提交详细信息', 'url': url_for('project.project_branch_commit_detail', project_branch_commit_id=project_branch_commit_id)})

    commit = ProjectBranchCommit.query.filter(ProjectBranchCommit.id == project_branch_commit_id).first()

    return render_template('project/android/project_branch_commit_detail.html', commit=commit)


@project.route('/project_branch_task_detail/<int:project_branch_task_id>', methods=['GET', 'POST'])
@login_required
def project_branch_task_detail(project_branch_task_id):
    """
    显示jira任务和commit的详细信息
    :param project_branch_task_id:
    :return:
    """

    task = ProjectBranchTask.query.filter(ProjectBranchTask.id == project_branch_task_id).first()
    if request.method == 'POST':
        task.category = request.form.get('category')
        db.session.add(task)
        db.session.commit()
        return redirect(url_for('project.project_branch_task_detail', project_branch_task_id=project_branch_task_id))

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '任务详细信息' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index + 1]
            break
    else:
        session['breadcrumbs'].append(
            {'text': '任务详细信息', 'url': url_for('project.project_branch_task_detail', project_branch_task_id=project_branch_task_id)})

    commits = [tmp.project_branch_commit for tmp in ProjectBranchTaskCommit.query.filter(ProjectBranchTaskCommit.project_branch_task_id == project_branch_task_id).all()]
    category_option = ['Public', 'Critical']

    return render_template('project/android/project_branch_task_detail.html', category_option=category_option, task=task, commits=commits)


@project.route('/project_branch_commit_compare/<int:first_project_code_id>/<int:second_project_code_id>')
@login_required
def project_branch_commit_compare(first_project_code_id, second_project_code_id):
    """
    比较两个分支将first上比second多的内容显示出来
    :param first_project_code_id:
    :param second_project_id:
    :return:
    """

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '分支比较' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index + 1]
            break
    else:
        session['breadcrumbs'].append(
            {'text': '分支比较', 'url': url_for('project.project_branch_commit_compare',
                                            first_project_code_id=first_project_code_id,
                                            second_project_code_id=second_project_code_id
                                            )})

    second_commits = [commit.commit_id for commit in
                      ProjectBranchCommit.query.filter(ProjectBranchCommit.project_code_id == second_project_code_id).all()]
    difference_commits = ProjectBranchCommit.query.filter(
        db.and_(ProjectBranchCommit.project_code_id == first_project_code_id,
                ~ProjectBranchCommit.commit_id.in_(second_commits)
                )).order_by(ProjectBranchCommit.commit_date.desc()).all()

    merge_commits = [tmp.commit_id for tmp in ProjectBranchMergeCommit.query.filter(
        db.and_(ProjectBranchMergeCommit.dest_project_code_id == first_project_code_id,
                ProjectBranchMergeCommit.src_project_code_id == second_project_code_id)).all()]
    result = []
    for commit in difference_commits:
        if commit.commit_id not in merge_commits:
            result.append(commit)

    first_branch  = ProjectCode.query.filter_by(id=first_project_code_id).first()
    second_branch  = ProjectCode.query.filter_by(id=second_project_code_id).first()

    return render_template('project/android/project_branch_commit_compare.html',
                           difference_commits=result,
                           first_branch=first_branch,
                           second_branch=second_branch
                           )


@project.route('/set_project_branch_commit_merge_commit', methods=['POST'])
@login_required
def set_project_branch_commit_merge_commit():
    """
    将commit标识为合并提交
    :return:
    """

    first_project_code_id = request.form.get('first_project_code')
    second_project_code_id = request.form.get('second_project_code')
    commit_id = request.form.get('commit_id')
    option = request.form.get('option')

    if option == 'on':
        merge_commit = ProjectBranchMergeCommit()
        merge_commit.commit_id = commit_id
        merge_commit.dest_project_code_id = first_project_code_id
        merge_commit.src_project_code_id = second_project_code_id
        merge_commit.meger_author_email = current_user.email

        db.session.add(merge_commit)
        db.session.commit()
    else:
        merge_commit = ProjectBranchMergeCommit.query.filter(
            db.and_(ProjectBranchMergeCommit.commit_id == commit_id,
                    ProjectBranchMergeCommit.dest_project_code_id == first_project_code_id,
                    ProjectBranchMergeCommit.src_project_code_id == second_project_code_id)).first()
        if merge_commit:
            db.session.delete(merge_commit)
            db.session.commit()

    return '{result: ture}'



@project.route('/jira_task_export_excel', methods=['POST'])
@login_required
def jira_task_export_excel():
    """
    jira task 导出成Excel
    :return:
    """

    task_ids = request.form.get('task_ids')
    project_tasks = ProjectBranchTask.query.filter(ProjectBranchTask.id.in_(task_ids.split(','))).all()
    excel_option = ExcelOptions(os.path.join(current_app.config['TMS_TEMP_DIR'], "%s_jira.xls" % current_user.username))
    excel_option.export_task_xls(project_tasks)

    return excel_option.file_path


@project.route('/commit_export_excel', methods=['POST'])
@login_required
def commit_export_excel():
    """
    jira task 导出成Excel
    :return:
    """

    commit_ids = request.form.get('commit_ids')
    commits = ProjectBranchCommit.query.filter(ProjectBranchCommit.id.in_(commit_ids.split(','))).all()

    excel_option = ExcelOptions(os.path.join(current_app.config['TMS_TEMP_DIR'], "%s_commits.xls" % current_user.username))
    excel_option.export_commit_xls(commits)

    return excel_option.file_path<file_sep>var project_table = $('#project_table').DataTable({
    "language": {
        "aria": {
            "sortAscending": ": activate to sort column ascending",
            "sortDescending": ": activate to sort column descending"
        },
        "emptyTable": "No data available in table",
        "info": "Showing _START_ to _END_ of _TOTAL_ records",
        "infoEmpty": "No records found",
        "infoFiltered": "(filtered1 from _MAX_ total records)",
        "lengthMenu": "Show _MENU_ records",
        "search": "Search:",
        "zeroRecords": "No matching records found",
        "paginate": {
            "previous": "Prev",
            "next": "Next",
            "last": "Last",
            "first": "First"
        }
    },
    "bFilter": true,
    "lengthMenu": [
        [10, 50, 200],
        [10, 50, 200] // change per page values here
    ],
    // set the initial value
    "pageLength": 10,
    "pagingType": "bootstrap_full_number"
});

// 关注项目
function start_project(project_id, start_option){
    $.getJSON(
        $SCRIPT_ROOT + "/developer/start_project",
        {'project_id': project_id, 'start_option': start_option},
        function(){
            if(start_option == 1) {
                $("#as"+project_id).hide();
                $("#ac"+project_id).show();
            }else{
                $("#as"+project_id).show();
                $("#ac"+project_id).hide();
            }
        }
    );
}

//Android项目关注、取消
function start_android_project(project_id, start_option){
    $.getJSON(
        $SCRIPT_ROOT + "/project/start_project",
        {'project_id': project_id, 'start_option': start_option},
        function(){
            if(start_option == 1) {
                $("#as"+project_id).hide();
                $("#ac"+project_id).show();
            }else{
                $("#as"+project_id).show();
                $("#ac"+project_id).hide();
            }
        }
    );
}

jQuery(document).ready(function() {
    $('#project_table tbody').on( 'dblclick', 'td', function () {
        var tdSeq = $(this).parent().find("td").index($(this)[0]);
        if(tdSeq == 8) {
            return;
        }else {
            $(this).toggleClass('selected');
            if ($(this).parent().attr('type')){
                window.location.href = $SCRIPT_ROOT + '/project/edit_android_project_page/' + $(this).parent().attr('data');
            }else{
                window.location.href = $SCRIPT_ROOT + '/developer/edit_project/' + $(this).parent().attr('data');
            }
        }
    });
    $("#project_table tr").live({
        mouseover:function(){
            $(this).css("cursor","pointer");
        }
    });

});<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""
<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

"""
将指定平台下所有的
Git仓库的分支、tag及commit信息导入到数据库中
"""
import sys
import os
from optparse import OptionParser

from GitRepository import GitRepository
from DBOption import DBOption

reload(sys)
sys.setdefaultencoding('utf-8')


def platform_repo(platform_path):
    """
    获取平台目录下所有的git仓库
    """

    repos = []
    for root_path, dirs, files in os.walk(platform_path):
        for dir_name in dirs:
            if dir_name == 'repo.git' or dir_name == '.repo' or dir_name == '.git' or root_path.find('/.repo/') > 0:
                    continue
            elif dir_name.endswith('.git'):
                print os.path.join(root_path, dir_name)
                repos.append(os.path.join(root_path, dir_name))

    return repos


def add_refs(db_option, repo, repository_id):
    """
    将repo中的分支和tag添加到数据库中
    """

    insert_data = []
    for head_name in repo.heads:
        insert_data.append((head_name, 1, repository_id))

    for tag_name in repo.tags:
        print 'tag_name:%s' % tag_name
        insert_data.append((tag_name, 0, repository_id))

    insert_refs_sql = "INSERT INTO repo_refs (name, category, repository_id) VALUES (%s, %s, %s)"
    db_option.insert_all(insert_refs_sql, insert_data)


def add_commits(db_option, repo, repository_id):
    """
    将repo中的分支和tag添加到数据库中
    """
    insert_data = []

    for commit in repo.log():
        print commit
        # 仓库的第一个提交没有parent
        if len(commit) == 9:
            insert_data.append((commit[0], '', commit[1], commit[2], commit[3], commit[4], commit[5], commit[6], commit[7], commit[8], repository_id))
        else:
            insert_data.append((commit[0], commit[1], commit[2], commit[3], commit[4], commit[5], commit[6], commit[7], commit[8], commit[9], repository_id))

    insert_commit_sql = "INSERT INTO repo_commit (commit_hash, parent_hash, tree_hash, author_name, author_email, " \
                        "author_time, committer_name, committer_email, commit_time, message, repository_id) " \
                        "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"

    db_option.insert_all(insert_commit_sql, insert_data)


def main():

    des = u'import Repository info'
    prog= u'AddRepository'
    ver = u'%prog v0.0.1'
    usage = u'%prog ip_address platform_path'

    parse = OptionParser(description=des, prog=prog, version=ver, usage=usage, add_help_option=True)

    parse.add_option('-t', '--server-ip', action='store', type='string', dest='ip_address',
                     help='repository server ip address')
    parse.add_option('-p', '--platform-path', action='store', type='string', dest='platform_path',
                     help='repository platform name')

    option, args = parse.parse_args()
    ip_address = option.ip_address
    platform_path = option.platform_path

    server_sql = "SELECT id FROM repo_server WHERE ip_address='%s'" % ip_address
    # mysql
    db_url = {'host': '192.168.33.7', 'db_name': 'tms', 'username': 'tup', 'password': 'tup'}
    db_option = DBOption(db_url, 'mysql')
    db_option.db_connect()
    server = db_option.fetch_one(server_sql)
    server_id = server[0]

    platform_sql = "SELECT id FROM repo_platform WHERE server_id=%s and path='%s'" % (server_id, platform_path)
    print "platform_sql:%s" % platform_sql
    platform = db_option.fetch_one(platform_sql)
    if platform:
        platform_id = platform[0]
    else:
        platform_insert_sql = "INSERT INTO repo_platform (name, path, server_id) VALUES ('%s', '%s', %s)" % \
                              (os.path.basename(platform_path), platform_path, server_id)
        print "platform_insert_sql:%s" % platform_insert_sql
        db_option.insert_one(platform_insert_sql)
        platform_id = db_option.fetch_one(platform_sql)

    for repo_path in platform_repo(platform_path):

        # 获取仓库数据库的id或添加仓库入数据库
        repo_sql = "SELECT id FROM repo_repo WHERE path='%s' and platform_id=%s" % (repo_path, platform_id)
        print "repo_sql:%s" % repo_sql
        repo_obj = db_option.fetch_one(repo_sql)
        if repo_obj:
            repo_id = repo_obj[0]
        else:
            insert_repo = "INSERT INTO repo_repo (name, path, platform_id) VALUE ('%s', '%s', %s)" % \
                          (os.path.basename(repo_path).split('.git')[0], repo_path, platform_id)
            print "insert_repo:%s" % insert_repo
            repo_id = db_option.insert_one(insert_repo)

        # 初始化Git仓库操作对象
        repo = GitRepository(repo_path)
        # 添加分支或tag入数据库
        add_refs(db_option, repo, repo_id)
        # 添加commit记录入数据库
        add_commits(db_option, repo, repo_id)


if __name__ == '__main__':
    from time import time
    start = time()
    main()
    print 'Total Time:%s' % str(time()-start)

<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

"""Excel文件读写操作"""

import os
import xlwt
import json
from flask import current_app
import commands
from tms.project.androidProjectModules import ProjectAndroid


class ExcelOptions:
    def __init__(self, file_path):
        self.file_path = file_path
        dir_path = os.path.dirname(self.file_path)
        if not os.path.exists(dir_path):
            os.makedirs(dir_path)

    def export_task_xls(self, records):
        """导出项目JIRA任务"""

        wb = xlwt.Workbook(encoding="utf8")
        ws = wb.add_sheet(u"项目已提交代码的JIRA任务")
        row = 0
        ws.write(row, 0, 'JIRA_KEY')
        ws.write(row, 1, 'Title')
        ws.write(row, 2, 'Description')
        ws.write(row, 3, 'Category')

        for record in records:
            row += 1
            ws.write(row, 0, record.task_key)
            ws.write(row, 1, record.summary)
            ws.write(row, 2, record.description)
            ws.write(row, 3, record.category)

        wb.save(self.file_path)


    def export_commit_xls(self, records):
        """导出项目提交记录"""

        wb = xlwt.Workbook(encoding="utf8")
        ws = wb.add_sheet(u"项目Commit")
        row = 0
        ws.write(row, 0, 'Repo')
        ws.write(row, 1, 'Message')
        ws.write(row, 2, 'Email')
        ws.write(row, 3, 'Name')
        ws.write(row, 4, 'Date')
        ws.write(row, 5, 'ID')

        for record in records:
            row += 1
            ws.write(row, 0, record.repo_path)
            ws.write(row, 1, record.message)
            ws.write(row, 2, record.commit_email)
            ws.write(row, 3, record.commit_name)
            ws.write(row, 4, record.commit_date)
            ws.write(row, 5, record.commit_id)

        wb.save(self.file_path)

    def export_jira_xls(self,jira_datas):
        """导出项目Jira统计数据"""

        temp=current_app.config['TMS_TEMP_DIR']
        if not os.path.isdir(temp+'/jira'):
            os.makedirs(os.path.join(str(commands.getoutput('pwd')), temp+'/jira'))
        os.chdir(os.path.join(str(commands.getoutput('pwd')), temp+'/jira'))

        wb = xlwt.Workbook(encoding="utf8")
        jira_datas = json.loads(jira_datas)

        ws = wb.add_sheet('total_tasks')
        row = 0
        ws.write(row, 0, 'status')
        ws.write(row, 1, 'total_num')
        for data in jira_datas['total_tasks']:
            row += 1
            ws.write(row, 0, data['status'])
            ws.write(row, 1, data['total_num'])

        # ws = wb.add_sheet('tasks_daily_update')
        # row = 0
        # ws.write(row, 0, 'date')
        # ws.write(row, 1, 'new_open')
        # ws.write(row, 2, 'closed')
        # for data in jira_datas['tasks_daily_update']:
        #     row += 1
        #     ws.write(row, 0, data['date'])
        #     ws.write(row, 1, data['new_open'])
        #     ws.write(row, 2, data['closed'])

        ws = wb.add_sheet('tasks_daily_summary')
        row = 0
        ws.write(row, 0, 'date')
        ws.write(row, 1, 'open_summary')
        ws.write(row, 2, 'closed_summary')
        for data in jira_datas['tasks_daily_summary']:
            row += 1
            ws.write(row, 0, data['date'])
            ws.write(row, 1, data['open_summary'])
            ws.write(row, 2, data['closed_summary'])

        ws = wb.add_sheet('daily_tasks_tester_open')
        row = 0
        ws.write(row, 0, 'creator')
        ws.write(row, 1, 'creatorNum')
        for data in jira_datas['daily_tasks_tester_open']:
            row += 1
            ws.write(row, 0, data['creator'])
            ws.write(row, 1, data['creatorNum'])

        ws = wb.add_sheet('daily_tasks_developer_closed')
        row = 0
        ws.write(row, 0, 'assigner')
        ws.write(row, 1, 'assignerNum')
        for data in jira_datas['daily_tasks_developer_closed']:
            row += 1
            ws.write(row, 0, data['assigner'])
            ws.write(row, 1, data['assignerNum'])

        ws = wb.add_sheet('total_tasks_tester_open')
        row = 0
        ws.write(row, 0, 'creator')
        ws.write(row, 1, 'creatorNum')
        for data in jira_datas['total_tasks_tester_open']:
            row += 1
            ws.write(row, 0, data['creator'])
            ws.write(row, 1, data['creatorNum'])

        ws = wb.add_sheet('total_tasks_developer_closed')
        row = 0
        ws.write(row, 0, 'assigner')
        ws.write(row, 1, 'assignerNum')
        for data in jira_datas['total_tasks_developer_closed']:
            row += 1
            ws.write(row, 0, data['assigner'])
            ws.write(row, 1, data['assignerNum'])

        wb.save(self.file_path)


    def export_common_issue_xls(self, records):
        """导出项目提交记录"""

        wb = xlwt.Workbook(encoding="utf8")
        ws = wb.add_sheet(u"项目共性问题")
        row = 0
        ws.write(row, 0, '问题项目名称')
        ws.write(row, 1, '平台名称')
        ws.write(row, 2, '基线名称')
        ws.write(row, 4, 'SPM')
        ws.write(row, 5, '原问题JIRA_KEY')
        ws.write(row, 6, '问题类别')
        ws.write(row, 7, '问题来源')
        ws.write(row, 8, '影响项目')
        ws.write(row, 9, '问题名称')
        ws.write(row, 10, '问题描述')

        for record in records:
            row += 1
            effect_project_list = ProjectAndroid.query.filter(ProjectAndroid.id.in_([tmp for tmp in
                                                                                record.effect_scope.split(',') if
                                                                                tmp.strip()])).all()
            effect_project = []
            for tmp in effect_project_list:
                if tmp.parent:
                    effect_project.append('%s_%s_%s' % (tmp.parent.name, tmp.android_version, tmp.name))
                else:
                    effect_project.append('%s_%s' % (tmp.android_version, tmp.name))

            project_obj = record.project
            ws.write(row, 0, project_obj.name)
            ws.write(row, 1, record.platform_name)
            ws.write(row, 2, record.baseline_name)
            ws.write(row, 3, record.spm.display_name)
            ws.write(row, 4, record.jira_key)
            ws.write(row, 5, record.category)
            ws.write(row, 6, record.issue_from)
            ws.write(row, 7, ','.join(effect_project))
            ws.write(row, 8, record.name)
            ws.write(row, 9, record.description)

        wb.save(self.file_path)
<file_sep>Flask==0.10.1 # 基础框架jinja2/werkzeug
Flask-Script==0.6.6 # 执行脚本
Flask-WTF==0.9.4 # Form 验证
Flask-HTTPAuth==2.2.0
Flask-Login==0.3.1 # 登陆
Flask-SQLAlchemy==1.0 # sqlalchemy orm
Flask-Migrate==1.1.0 # model版本管理
Flask-Mail==0.9.0 # 邮件
Flask-Moment==0.2.1 #
Flask-SSLify # https 协议
Flask-excel #Excel 操作
#pandas # 解析从JIRA中导出的Excel
#html5lib
#BeautifulSoup4

ForgeryPy #
itsdangerous #
paramiko # ssh 连接
mysql-python # mysql连接
coverage #
simplejson
xlwt
pyexcel-xls # xls读库
pyexcel-xlsx # xlsx读库
jira  #jira连接库
uwsgi
#celery # 任务异步处理队列
#redis  # key-value 数据库
<file_sep>#!/usr/bin/python
# -*- coding:utf-8 -*-

import os.path
from ftplib import FTP


class MyFTP:

    def __init__(self, host, username, password, post=21, buf_size=1024):
        # 设置缓冲块大小
        self.buf_size = buf_size
        self.ftp = FTP()
        # 打开调试级别2，显示详细信息;0为关闭调试信息
        self.ftp.set_debuglevel(2)
        # 连接
        self.ftp.connect(host, post)
        # 登录，如果匿名登录则用空串代替即可
        self.ftp.login(username, password)
        # print ftp.getwelcome()#显示ftp服务器欢迎信息
        # ftp.cwd('xxx/xxx/') #选择操作目录

    def ftp_up(self, file_path):
        """上传文件"""

        # 以读模式在本地打开文件
        file_handler = open(file_path, 'rb')
        # 上传文件
        self.ftp.storbinary('STOR %s' % os.path.basename(file_path), file_handler, self.buf_size)
        self.ftp.set_debuglevel(0)
        file_handler.close()

    def ftp_down(self, local_path, ftp_path):
        """文件下载"""

        file_name = os.path.basename(ftp_path)
        # 以写模式在本地打开文件
        file_handler = open(os.path.join(local_path, file_name), 'wb').write
        # 接收服务器上文件并写入本地文件
        self.ftp.retrbinary('RETR %s' % ftp_path, file_handler, self.buf_size)
        self.ftp.set_debuglevel(0)

    def quit(self):
        self.ftp.quit()
        print "ftp OK"

if __name__ == '__main__':
    my_ftp = MyFTP('192.168.32.83', 'jenkins', 'jenkins')
    my_ftp.ftp_down('/home/roy/tmp', '/应用开发/ApeAssistivePoint/基于8400小白点v4.rar')
<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""
from flask import Blueprint

department = Blueprint('department', __name__)
from . import views
<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""


def convert_to_dict(obj):
    """object对象转换成Dict对象"""

    obj_dict = {}
    obj_dict.update(obj.__dict__)
    return obj_dict


def convert_to_dicts(objs):
    """把对象列表转换成字典列表"""

    obj_arr = []

    for o in objs:
        obj_dict = {}
        obj_dict.update(o.__dict__)
        obj_arr.append(obj_dict)

    return obj_arr


def class_to_dict(obj):
    """把对象(单个对象、list、set)转出字典"""

    is_list = obj.__class__ == [].__class__
    is_set = obj.__class__ == set().__class__

    if is_list or is_set:
        obj_arr = []

        for o in obj:
            obj_dict = {}
            obj_dict.update(o.__dict__)
            obj_arr.append(obj_dict)

        return obj_arr
    else:
        obj_dict = {}
        obj_dict.update(obj.__dict__)
        return obj_dict
<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""
from flask import render_template, redirect, url_for, request, current_app
from flask.ext.login import login_required, current_user

from . import main


@main.route('/', methods=['GET', 'POST'])
@login_required
def index():
    """按不同的角色定位到不同的"""

    return redirect(url_for(current_user.role.index_page))
<file_sep># -*- coding:utf-8 -*-
__author__ = 'android'


import json

from flask import render_template, redirect, url_for, request, flash, current_app, session, send_file
from flask.ext.login import login_user, logout_user, login_required, current_user
from flask.ext.mail import Mail, Message
from functools import update_wrapper
from datetime import datetime, timedelta
from .import report
from tms import db, mail
from tms.auth.models import User
from tms.project.androidProjectModules import *
from tms.auth.models import SystemParameter
from tms.baseline.models import BPlatform
from tms.report.models import project_task_type,project_task_list
import xlwt
import os
from simplejson import loads as json_loads, dumps as json_dumps
import commands
import flask_excel as excel
from tms.auth.models import User
import shutil




@report.route('/task_type_page')
@login_required
def task_type_page():
    """查询所有任务类型"""


    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '我的任务', 'url': url_for('report.my_task_page')})
    session['menu_path'] = ['周报日报', '类型管理']
    project_task_types = project_task_type.query.all()
    return render_template('report/task_type_page.html',project_task_types=project_task_types)

@report.route('/week_task_page')
@login_required
def week_task_page():
    """周报，查询当天前7天的任务"""
    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '我的任务', 'url': url_for('report.my_task_page')})
    session['menu_path'] = ['周报日报', '我的周报']
    d = datetime.now()
    d1 = d + timedelta(days=-7)
    user_id=session['user']["id"]
    project_task_lists=project_task_list.query.filter(db.and_(project_task_list.starttime>=d1),(project_task_list.starttime<=d),(project_task_list.tms_user==user_id)).order_by(project_task_list.id.desc()).all()

    time=datetime.now()
    for project_task_list1 in project_task_lists:
            project_task_list1.state=(time-project_task_list1.updatetime).days
    return render_template('report/week_task_page.html',project_task_lists=project_task_lists)

@report.route('/month_task_page')
@login_required
def month_task_page():
    """月报，查询当前月的任务"""

    user_id=session['user']["id"]

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '我的任务', 'url': url_for('report.my_task_page')})
    session['menu_path'] = ['周报日报', '我的月报']
    i =datetime.now()
    month=str(i.month)
    minmonth=str(i.year)+"-"+str(i.month)+"-01"
    maxmonth=str(i.year)+"-"+str(i.month)+"-31"

    project_task_lists = project_task_list.query.filter(db.and_(project_task_list.starttime>=minmonth),(project_task_list.starttime<=maxmonth),(project_task_list.tms_user==user_id)).order_by(project_task_list.id.desc()).all()

    time=datetime.now()
    for project_task_list1 in project_task_lists:
            project_task_list1.state=(time-project_task_list1.updatetime).days
            # db.session.add(project_task_list1)
            # db.session.commit()
    return render_template('report/month_task_page.html',project_task_lists=project_task_lists,month=month)

@report.route('/all_task_page',methods=['GET', 'POST'])
@login_required
def all_task_page():
    """查询所有任务"""
    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '我的任务', 'url': url_for('report.my_task_page')})
    session['menu_path'] = ['周报日报', '全部任务']

    users=get_users()
    types=get_types()


    project_task_lists = project_task_list.query.order_by(project_task_list.id.desc()).all()
    time=datetime.now()
    for project_task_list1 in project_task_lists:
            project_task_list1.state=(time-project_task_list1.updatetime).days

    return render_template('report/all_task_page.html',project_task_lists=project_task_lists,querytype=0,users=users,types=types)

@report.route('/preadd_task_type',methods=['GET', 'POST'])
@login_required
def preadd_task_type():
    """准备添加任务类型，实现页面跳转"""
    return render_template('report/add_task_type.html')


@report.route('/add_task_type',methods=['GET', 'POST'])
@login_required
def add_task_type():
    """添加任务类型"""
    task_type=project_task_type()
    task_type.task_type=request.form.get('task_type')
    task_type.state=request.form.get('state')
    db.session.add(task_type)
    db.session.commit()
    project_task_types = project_task_type.query.all()

    return render_template('report/task_type_page.html',project_task_types=project_task_types)



@report.route('/query_all_task_list',methods=['GET', 'POST'])
@login_required
def query_all_task_list():
    """全部任务，根据人物，类型，状态，时间  模糊查询"""

    starttime=request.form.get('starttime').strip()
    endtime=request.form.get('endtime').strip()
    task_type=request.form.get('task_type').strip()
    the_user=request.form.get('the_user').strip()
    state=request.form.get('state').strip()
    # print starttime+endtime+task_type+the_user+state
    users=get_users()
    types=get_types()


    search_sql = " SELECT t.id,ptt.task_type,t.taskname,t.starttime,t.updatetime,t.question_log,t.SCHEDULE schedules,t.state,tu.display_name " \
                 "FROM project_task_list t ,project_task_type ptt,tms_user tu WHERE t.task_type = ptt.id AND t.tms_user = tu.id"
    if(starttime!="" and endtime!=""):
        search_sql+=" AND t.starttime >="+"'"+starttime+"'"+"AND t.starttime<="+"'"+endtime+"'"
    if(task_type!='0' and  task_type!=""):
        search_sql+=" AND ptt.id ="+task_type
    if(the_user!='0' and  the_user!=""):
        search_sql+=" AND tu.id="+the_user
    if(state!='0' and  state!=""):
        if(state=='1'):
            search_sql+=" AND t.state<=3 AND t.SCHEDULE!=100"
        elif(state=='2'):
            search_sql+=" AND t.state>3 AND t.SCHEDULE!=100"
        else:
            search_sql+=" AND t.schedule=100"

    search_sql+=' ORDER BY t.id desc '

    project_task_lists = db.session.execute(search_sql).fetchall()

    return render_template('report/all_task_page.html',project_task_lists=project_task_lists,starttime=starttime,endtime=endtime,task_type=task_type,querytype=1,the_user=the_user,state=state,users=users,types=types)


@report.route('/query_week_task_list',methods=['GET', 'POST'])
@login_required
def query_week_task_list():
    """周报按照时间模糊查询"""

    starttime=request.form.get('starttime','').strip()
    endtime=request.form.get('endtime','').strip()

    user_id=session['user']["id"]
    d = datetime.now()
    d1 = d + timedelta(days=-7)
    if(starttime =='' or endtime==''):
        project_task_lists=project_task_list.query.filter(db.and_(project_task_list.starttime>=d1),(project_task_list.starttime<=d),(project_task_list.tms_user==user_id)).order_by(project_task_list.id.desc()).all()
        return render_template('report/week_task_page.html',project_task_lists=project_task_lists,starttime1=starttime,endtime1=endtime)
    else:
        project_task_lists = project_task_list.query.filter(db.and_(project_task_list.starttime>=starttime),(project_task_list.starttime<=endtime),(project_task_list.tms_user==user_id)).order_by(project_task_list.id.desc()).all()
        return render_template('report/week_task_page.html',project_task_lists=project_task_lists,starttime1=starttime,endtime1=endtime)

@report.route('/query_month_task_list',methods=['GET', 'POST'])
@login_required
def query_month_task_list():
    """按照月模糊查询"""
    select_month=request.form.get('select_month').strip()
    i =datetime.now()
    minmonth=str(i.year)+"-"+str(select_month)+"-01"
    maxmonth=str(i.year)+"-"+str(select_month)+"-31"
    user_id=session['user']["id"]

    month=select_month;
    project_task_lists = project_task_list.query.filter(db.and_(project_task_list.starttime>=minmonth),(project_task_list.starttime<=maxmonth),(project_task_list.tms_user==user_id)).order_by(project_task_list.id.desc()).all()
    return render_template('report/month_task_page.html',project_task_lists=project_task_lists,month=month)


@report.route('/pre_update_my_task',methods=['GET', 'POST'])
@login_required
def pre_update_my_task():
    """获取要更新的任务"""
    id1=request.form.get('id1')
    state1=request.form.get('state1')
    project_task_list1=project_task_list.query.filter(project_task_list.id == id1).first()

    return render_template('report/update_task_page.html',project_task_list1=project_task_list1,state1=state1)

@report.route('/update_task_type',methods=['GET', 'POST'])
@login_required
def update_task_type():
    """修改类型"""

    new_array = request.form['new_array']


    array =  str(new_array).split(";")

    for arr in array :

        if arr:

            arr_array = str(arr).split(',')
            id = arr_array[0]
            id = str(id).replace('id_state_','')
            state = arr_array[1]
            task_type = project_task_type.query.filter(project_task_type.id == id).first()
            task_type.state = state
            db.session.add(task_type)

    db.session.commit()


    project_task_types = project_task_type.query.all()

    return render_template('report/task_type_page.html',project_task_types=project_task_types)




@report.route('/update_my_task',methods=['GET', 'POST'])
@login_required
def update_my_task():
    """更新任务"""
    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '我的任务', 'url': url_for('report.my_task_page')})
    session['menu_path'] = ['周报日报', '我的任务']

    id=request.form.get('id')
    task_schedule=request.form.get('tdescribe')
    question_log=request.form.get('question_log')
    user_id=session['user']["id"]

    my_task = project_task_list.query.filter(project_task_list.id == id).first()
    my_task.schedule=task_schedule
    my_task.question_log=question_log
    my_task.updatetime=datetime.now()
    db.session.add(my_task)
    db.session.commit()
    project_task_lists = project_task_list.query.filter(db.and_(project_task_list.tms_user==user_id),(project_task_list.schedule!=100)).all()
    return render_template('report/my_task_page.html',project_task_lists=project_task_lists,state=2)


@report.route('/my_task_page')
@login_required
def my_task_page():
    """查询我的任务"""
    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '我的任务', 'url': url_for('report.my_task_page')})
    session['menu_path'] = ['周报日报', '我的任务']
    time=datetime.now()
    user_id=session['user']["id"]
    project_task_lists = project_task_list.query.filter(db.and_(project_task_list.tms_user==user_id),(project_task_list.schedule!=100)).order_by(project_task_list.id.desc()).all()
    for project_task_list1 in project_task_lists:
        if(project_task_list1.state!=(time-project_task_list1.updatetime).days):
            project_task_list1.state=(time-project_task_list1.updatetime).days
            db.session.add(project_task_list1)
            db.session.commit()
    return render_template('report/my_task_page.html',project_task_lists=project_task_lists)

@report.route('/add_task_page',methods=['GET', 'POST'])
@login_required
def add_task_page():
    """增加任务"""

    time=datetime.now()
    id=session['user']["id"]
    new_task =project_task_list()
    new_task.task_type=request.form.get('task_type','').strip()
    new_task.taskname=request.form.get('taskname','').strip()
    new_task.schedule=request.form.get('schedule','').strip()
    new_task.tdescribe=request.form.get('tdescribe','').strip()
    new_task.question_log=request.form.get('question_log')
    new_task.updatetime=time
    new_task.starttime=time
    new_task.state=(new_task.updatetime)-(new_task.starttime)
    new_task.tms_user=id
    db.session.add(new_task)
    db.session.commit()
    user_id=session['user']["id"]
    project_task_lists = project_task_list.query.filter(db.and_(project_task_list.tms_user==user_id),(project_task_list.schedule!=100)).all()
    return render_template('report/my_task_page.html',project_task_lists=project_task_lists,state=1)


@report.route('/preadd_task_page')
@login_required
def preadd_task_page():
    """准备增加任务需要的数据"""
    project_task_types = project_task_type.query.filter(project_task_type.state==1).all()
    return render_template('report/add_task_page.html',project_task_types=project_task_types)


def get_users():
    """获取登录用户所在组的成员"""
    list = []

    user_role_id=session['user']["role_id"]
    users=User.query.filter(User.role_id==user_role_id)
    for user in users:
        list.append({'id': user.id, 'display_name': user.display_name})
    return list


def get_types():
    """获取任务类型"""
    types = []
    obj1 = project_task_type.query.all()
    for obj in obj1:
        types.append({'id': obj.id, 'task_type': obj.task_type})

    return types

@report.route('/download_week_list',methods=['GET', 'POST'])
@login_required
def download_week_list():
    """将周报的数据导出到Excle"""
    starttime=request.form['starttime']
    endtime=request.form['endtime']
    user_id=session['user']["id"]
    d = datetime.now()
    d1 = d + timedelta(days=-7)

    workbook=xlwt.Workbook(encoding='utf-8')

    booksheet=workbook.add_sheet('Sheet 1', cell_overwrite_ok=True)
    booksheet.col(1).width=400*20
    booksheet.col(2).width=300*20
    booksheet.col(3).width=400*20
    booksheet.col(6).width=500*20
    booksheet.col(7).width=500*20
    if(starttime =='' or endtime==''):
        project_task_lists=project_task_list.query.filter(db.and_(project_task_list.starttime>=d1),(project_task_list.starttime<=d),(project_task_list.tms_user==user_id))
    else:
        project_task_lists = project_task_list.query.filter(db.and_(project_task_list.starttime>=starttime),(project_task_list.starttime<=endtime),(project_task_list.tms_user==user_id)).all()


    DATA=[('年份','开始时间','任务类型','任务名称','任务进度','任务人','描述','问题记录'),]
    for list in project_task_lists:
        DATA.append((str(d.year),str(list.starttime),str(list.type_1.task_type),list.taskname,str(list.schedule)+'%',list.user1.display_name,list.tdescribe,list.question_log))
    for i,row in enumerate(DATA):
        for j,col in enumerate(row):
            booksheet.write(i,j,col)
    user_name=User.query.filter(User.id==user_id).first().username
    excel_name='my_task'+str(user_id)+'.xls'
    workbook.save(excel_name)
    file_name=current_app.config['TMS_TEMP_DIR']
    basefile= str(commands.getoutput('pwd'))
    if os.path.isdir(file_name):
        pass
    else:
        os.mkdir(os.path.join(basefile, 'temp'))
    if os.path.isdir(file_name+"/"+user_name):
        pass
    else:
        os.mkdir(os.path.join(file_name, user_name))
    if os.path.isfile(file_name+"/"+user_name+"/"+excel_name):
        os.remove(file_name+"/"+user_name+"/"+excel_name)
        shutil.move(basefile+"/"+excel_name,file_name+"/"+user_name+"/")
    else:
        shutil.move(basefile+"/"+excel_name,file_name+"/"+user_name+"/")
    return file_name+'/'+user_name+'/'+excel_name


@report.route('/download_month_list',methods=['GET', 'POST'])
@login_required
def download_month_list():
    """将月报的数据导出到Excle"""
    select_month=request.form['select_month']
    user_id=session['user']["id"]
    d = datetime.now()
    minmonth=str(d.year)+"-"+str(select_month)+"-01"
    maxmonth=str(d.year)+"-"+str(select_month)+"-31"
    user_id=session['user']["id"]
    project_task_lists = project_task_list.query.filter(db.and_(project_task_list.starttime>=minmonth),(project_task_list.starttime<=maxmonth),(project_task_list.tms_user==user_id))

    workbook=xlwt.Workbook(encoding='utf-8')

    booksheet=workbook.add_sheet('Sheet 1', cell_overwrite_ok=True)
    booksheet.col(1).width=400*20
    booksheet.col(2).width=300*20
    booksheet.col(3).width=400*20
    booksheet.col(6).width=500*20
    booksheet.col(7).width=500*20

    DATA=[('年份','开始时间','任务类型','任务名称','任务进度','任务人','描述','问题记录'),]
    for list in project_task_lists:
        DATA.append((str(d.year),str(list.starttime),str(list.type_1.task_type),list.taskname,str(list.schedule)+'%',list.user1.display_name,list.tdescribe,list.question_log))
    for i,row in enumerate(DATA):
        for j,col in enumerate(row):
            booksheet.write(i,j,col)
    user_name=User.query.filter(User.id==user_id).first().username
    excel_name='my_task'+str(user_id)+'.xls'
    workbook.save(excel_name)
    file_name=current_app.config['TMS_TEMP_DIR']
    basefile= str(commands.getoutput('pwd'))
    if os.path.isdir(file_name):
        pass
    else:
        os.mkdir(os.path.join(basefile, 'temp'))
    if os.path.isdir(file_name+"/"+user_name):
        pass
    else:
        os.mkdir(os.path.join(file_name, user_name))
    if os.path.isfile(file_name+"/"+user_name+"/"+excel_name):
        os.remove(file_name+"/"+user_name+"/"+excel_name)
        shutil.move(basefile+"/"+excel_name,file_name+"/"+user_name+"/")
    else:
        shutil.move(basefile+"/"+excel_name,file_name+"/"+user_name+"/")

    return file_name+'/'+user_name+'/'+excel_name


@report.route('/download_alltask_list',methods=['GET', 'POST'])
@login_required
def download_alltask_list():
    """将全部任务的数据导出到Excle"""
    starttime=request.form['starttime']
    endtime=request.form['endtime']
    task_type=request.form['task_type']
    the_user=request.form['the_user']
    state=request.form['state']
    user_id=session['user']["id"]
    d = datetime.now()
    d1 = d + timedelta(days=-7)
    workbook=xlwt.Workbook(encoding='utf-8')

    booksheet=workbook.add_sheet('Sheet 1', cell_overwrite_ok=True)
    booksheet.col(1).width=400*20
    booksheet.col(2).width=300*20
    booksheet.col(3).width=400*20
    booksheet.col(6).width=500*20
    booksheet.col(7).width=500*20
    search_sql = " SELECT t.id,ptt.task_type,t.taskname,t.starttime,t.updatetime,t.question_log,t.tdescribe,t.SCHEDULE schedules,t.state,tu.display_name " \
                 "FROM project_task_list t ,project_task_type ptt,tms_user tu WHERE t.task_type = ptt.id AND t.tms_user = tu.id"
    if(starttime!="" and endtime!=""):
        search_sql+=" AND t.starttime >="+"'"+starttime+"'"+"AND t.starttime<="+"'"+endtime+"'"
    if(task_type!='0' and  task_type!=""):
        search_sql+=" AND ptt.id ="+task_type
    if(the_user!='0' and  the_user!=""):
        search_sql+=" AND tu.id="+the_user
    if(state!='0' and  state!=""):
        if(state=='1'):
            search_sql+=" AND t.state<=3 AND t.SCHEDULE!=100"
        elif(state=='2'):
            search_sql+=" AND t.state>3 AND t.SCHEDULE!=100"
        else:
            search_sql+=" AND t.schedule=100"

    # print search_sql

    project_task_lists = db.session.execute(search_sql).fetchall()

    DATA=[('年份','开始时间','任务类型','任务名称','任务进度','任务人','描述','问题记录'),]
    for list in project_task_lists:
        DATA.append((str(d.year),str(list.starttime),str(list.task_type),list.taskname,str(list.schedules)+'%',list.display_name,list.tdescribe,list.question_log))
    for i,row in enumerate(DATA):
        for j,col in enumerate(row):
            booksheet.write(i,j,col)
    user_name=User.query.filter(User.id==user_id).first().username
    excel_name='my_task'+str(user_id)+'.xls'
    workbook.save(excel_name)
    file_name=current_app.config['TMS_TEMP_DIR']
    basefile= str(commands.getoutput('pwd'))
    if os.path.isdir(file_name):
        pass
    else:
        os.mkdir(os.path.join(basefile, 'temp'))
    if os.path.isdir(file_name+"/"+user_name):
        pass
    else:
        os.mkdir(os.path.join(file_name, user_name))
    if os.path.isfile(file_name+"/"+user_name+"/"+excel_name):
        os.remove(file_name+"/"+user_name+"/"+excel_name)
        shutil.move(basefile+"/"+excel_name,file_name+"/"+user_name+"/")
    else:
        shutil.move(basefile+"/"+excel_name,file_name+"/"+user_name+"/")

    return file_name+'/'+user_name+'/'+excel_name
<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'
"""
工程配置
"""

import os

base_dir = os.path.abspath(os.path.dirname(__file__))


class Config:
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'tinno flask key'
    SSL_DISABLE = False
    SQLALCHEMY_COMMIT_ON_TEARDOWN = True
    SQLALCHEMY_RECORD_QUERIES = True
    SQLALCHEMY_TRACK_MODIFICATIONS = True

    # 邮箱信息
    MAIL_SERVER = '192.168.10.8'
    MAIL_PORT = 25
    MAIL_USER_TLS = False
    MAIL_USERNAME = '<EMAIL>'
    MAIL_PASSWORD = '<PASSWORD>'
    TMS_MAIL_SUBJECT_PREFIX = '[TMS]'
    TMS_MAIL_SENDER = 'Tinno SCM<<EMAIL>>'
    TMS_ADMIN = os.environ.get('TMS_ADMIN') or '<EMAIL>'

    # JIRA数据库信息
    JIRA_DB_HOST = '172.16.5.95'
    JIRA_DB_NAME = 'jiradb6'
    JIRA_DB_USER = 'jirauser'
    JIRA_DB_PASSWD = '<PASSWORD>'

    TMS_POSTS_PER_PAGE = 20
    TMS_FOLLOWERS_PRE_PAGE = 50
    TMS_COMMENTS_PER_PAGE = 30
    TMS_SLOW_DB_QUERY_TIM = 0.5
    UPLOAD_FOLDER = os.path.join(base_dir, 'upload')
    TMS_TEMP_DIR = os.path.join(base_dir, 'temp')
    RELEASE_REPO_FOLDER = os.path.join(base_dir, 'tinno_release')
    MIME_TYPE = {
        '.xls':  'application/vnd.ms-excel',
        '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        '.txt':  'text/plain',
        '.pdf':  'application/pdf',
        '.doc':  'application/msword',
        '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
        '.ppt':  'application/vnd.ms-powerpoint',
        '.rar':  'application/rar',
        '.zip':  'application/zip',
        '.tar':  'application/x-tar',
        '.gz':   'application/x-gzip'
    }

    JIRA_SERVER = 'http://172.16.5.95:8080'
    @staticmethod
    def init_app(app):
        pass


class DevelopmentConfig(Config):
    """开发环境配置"""

    DEBUG = True
    SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \
                              'mysql://tup:tup@192.168.33.7:3306/tms?charset=utf8'

    @classmethod
    def init_app(cls, app):
        Config.init_app(app)

        # 配置日志文件
        import logging
        import datetime

        # 配置日志文件
        formatter = logging.Formatter('%(asctime)s %(name)s[line:%(lineno)d]:%(levelname)s %(message)s')

        # 输出到文件
        log_file = os.path.join(base_dir, 'logs', datetime.datetime.now().strftime('%Y-%m-%d'), 'tms.log')
        if not os.path.exists(os.path.dirname(log_file)):
            os.makedirs(os.path.dirname(log_file))

        file_handler = logging.FileHandler(log_file)
        file_handler.setLevel(logging.DEBUG)
        file_handler.setFormatter(formatter)
        app.logger.addHandler(file_handler)

        # 输出到控制台
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.DEBUG)
        console_handler.setFormatter(formatter)
        app.logger.addHandler(console_handler)
        app.logger.info('Developement')


class ProductionConfig(Config):
    """生产环境"""

    DEBUG = False
    SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or 'mysql://tup:tup@192.168.33.7:3306/tms?charset=utf8'


    @classmethod
    def init_app(cls, app):
        Config.init_app(app)

        cls.TMS_TEMP_DIR = '/var/www/tmp'
        # 配置日志文件
        import logging
        import datetime
        from logging.handlers import SMTPHandler

        logging.basicConfig(
            level=logging.DEBUG,
            format='%(asctime)s %(name)s[line:%(lineno)d]:%(levelname)s %(message)s',
        )

        # 输出到文件
        log_file = os.path.join(base_dir, 'logs', datetime.datetime.now().strftime('%Y-%m-%d'), 'tms.log')
        if not os.path.exists(os.path.dirname(log_file)):
            os.makedirs(os.path.dirname(log_file))

        file_handler = logging.FileHandler(log_file)
        file_handler.setLevel(logging.WARNING)
        app.logger.addHandler(file_handler)

        # 输出到控制台
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.DEBUG)
        app.logger.addHandler(console_handler)

        # 将系统错误邮件通知给管理员
        credentials = None
        secure = None
        if getattr(cls, 'MAIL_USERNAME', None) is not None:
            credentials = (cls.MAIL_USERNAME, cls.MAIL_PASSWORD)
            if getattr(cls, 'MAIL_USE_TLS', None):
                secure = ()

        mail_handler = SMTPHandler(
            mailhost=(cls.MAIL_SERVER, cls.MAIL_PORT),
            fromaddr=cls.TMS_MAIL_SENDER,
            toaddrs=[cls.TMS_ADMIN],
            subject=cls.TMS_MAIL_SUBJECT_PREFIX + 'Application Error',
            credentials=credentials,
            secure=secure,
        )
        mail_handler.setLevel(logging.ERROR)
        app.logger.addHandler(mail_handler)


config = {
    'production': ProductionConfig,
    'default': DevelopmentConfig,
}
<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""
import commands


def update_release_repository(current_user, logger, version):
    """提交发布库"""

    # 更新发布库
    add_cmd = 'git add .'
    result, output = commands.getstatusoutput(add_cmd)
    # 非零就是出错了
    if result:
        return False, u'发布库执行git add .命令出错了\n{0}\n{1}'.format(result, output)

    commit_cmd = 'git commit -m "<REQ><SCM-10><release {0} {1} by {2}>"'.format(version.name, version.version_number,
                                                                                current_user.email)
    result, output = commands.getstatusoutput(commit_cmd)
    # 非零就是出错了
    if result:
        logger.info(commit_cmd)
        logger.info(output)
        return False, u'发布库执行\n{0}命令出错了\n{1}\n{2}'.format(commit_cmd, result, output)

    create_tag_cmd = 'git tag {0}'.format(version.version_number)
    result, output = commands.getstatusoutput(create_tag_cmd)
    # 非零就是出错了
    if result:
        logger.info(commit_cmd)
        logger.info(output)
        return False, u'创建版本Tag\n {0} 命令出错了\n{1}\n{2}'.format(create_tag_cmd, result, output)

    # push_master = 'git push origin master'
    # result, output = commands.getstatusoutput(push_master)
    # # 非零就是出错了
    # if result:
    #     logger.info(commit_cmd)
    #     logger.info(output)
    #     return False, u'push master 分支命令出错了\n{0}\n{1}'.format(result, output)
    #
    # push_tag = 'git push origin {0}'.format(version.version_number)
    # result, output = commands.getstatusoutput(push_tag)
    # # 非零就是出错了
    # if result:
    #     logger.info(commit_cmd)
    #     logger.info(output)
    #     return False, u'push 版本tag\n {0} 命令出错了\n{1}\n{2}'.format(push_tag, result, output)

    return True, ''
<file_sep>[uwsgi]
socket=127.0.0.1:8001
chdir=/var/www/tms
wsgi-file=manage.py
callable=app
process=1
threads=2
stats=127.0.0.1:8181
buffer-size=55350
logdata=true
limit-as=6048
daemnize=/var/www/tms/logs/tms.log

uwsgi_connect_timeout=75
uwsgi_read_timeout=1200
uwsgi_send_timeout=1200
harakiri=2000
<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""
from tms import db


class TestCase(db.Model):
    """测试用例"""

    __tablename__ = 'tms_test_case'

    id = db.Column(db.Integer, primary_key=True)
    # 用例编号
    number = db.Column(db.String(64), unique=True, index=True)
    # 用例等级
    level = db.Column(db.String(20), index=True)
    # 测试类型
    test_type = db.Column(db.String(64), default='功能')
    # 功能模块(对应用例,用项目JIRA_KEY关联)
    module_name = db.Column(db.String(64), index=True)
    # 用例实现代码类名
    class_name = db.Column(db.String(64))
    # 方法名
    method_name = db.Column(db.String(64))
    # 测试功能
    test_fun = db.Column(db.String(255))
    # test目的
    test_target = db.Column(db.String(255))
    # 是否用标准SDK开发
    standard_sdk = db.Column(db.Boolean, default=True)
    # 初始条件
    test_init = db.Column(db.String(255))
    # 测试步骤
    test_step = db.Column(db.String(255))
    # 预期结果
    expect_result = db.Column(db.String(255))
    # 用例状态
    activate = db.Column(db.Boolean, default=True)
    # 添加人
    add_user_id = db.Column(db.Integer, db.ForeignKey('tms_user.id'))
    add_user = db.relationship('User', foreign_keys=[add_user_id], backref=db.backref('case_add_user', order_by=id))
    add_time = db.Column(db.DateTime())
    # 最新修改人
    last_user_id = db.Column(db.Integer, db.ForeignKey('tms_user.id'))
    last_user = db.relationship('User', foreign_keys=[last_user_id], backref=db.backref('last_last_user', order_by=id))
    # 最新修改时间
    last_update_time = db.Column(db.DateTime())

    def __repr__(self):
        return '<TestCase %r>' % self.number


class TestCaseLog(db.Model):
    """测试用例修改记录"""

    __tablename__ = 'tms_test_case_log'

    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('tms_user.id'))
    user = db.relationship('User')
    test_case_id = db.Column(db.Integer, db.ForeignKey('tms_test_case.id'))
    test_case = db.relationship('TestCase', backref=db.backref('modify_log', order_by=id))
    update_time = db.Column(db.DateTime())
    content = db.Column(db.String(500))

    def __repr__(self):
        return '<TestCaseLog %r>' % self.test_case.number
<file_sep>/**
 * Created by changjie.fan on 2016/5/6.
 */

var project_myos_app_table = $('#project_myos_app_table').DataTable({
    "language": {
        "aria": {
            "sortAscending": ": activate to sort column ascending",
            "sortDescending": ": activate to sort column descending"
        },
        "emptyTable": "No data available in table",
        "info": "Showing _START_ to _END_ of _TOTAL_ records",
        "infoEmpty": "No records found",
        "infoFiltered": "(filtered1 from _MAX_ total records)",
        "lengthMenu": "Show _MENU_ records",
        "search": "Search:",
        "zeroRecords": "No matching records found",
        "paginate": {
            "previous": "Prev",
            "next": "Next",
            "last": "Last",
            "first": "First"
        }
    },
    "bFilter": true,
    "lengthMenu": [
        [10, 50, 200],
        [10, 50, 200] // change per page values here
    ],
    // set the initial value
    "pageLength": 10,
    "pagingType": "bootstrap_full_number"
});

$('#project_myos_app_table tbody').on('dblclick', 'tr', function () {
    window.location.href = $SCRIPT_ROOT + '/project/project_myos_app_edit/' + project_id + '/' + $(this).attr('data');
});


// select all
$('#selectAll').on('click', function () {
    if ($('#selectAll').parent().attr('class') == 'checked') {
        $('input[type="checkbox"]', $('#project_myos_app_table tbody')).each(function () {
            $(this).parent().removeClass("checked");
        });
    } else {
        $('input[type="checkbox"]', $('#project_myos_app_table tbody')).each(function () {
            $(this).parent().addClass("checked");
        });
    }
});
// 行选择
$('#project_myos_app_table tbody').on('change', 'input[type="checkbox"]', function () {
    if($(this).parent().attr('class') == 'checked'){
        $('#selectAll').parent().removeClass("checked");
        $(this).parent().removeClass("checked");
    } else {
        $(this).parent().addClass("checked");
    }
});

// 删除项目配置的myos应用
function delete_project_app_config(project_id){
    var app_ids = new Array();
    $('#project_myos_app_table  td span').each(function () {
        if ($(this).attr('class') && $(this).attr('class') == 'checked') {
            app_ids.push($($($(this).parent()).parent()).parent().attr('data'));
        }
    });

    if (!app_ids || app_ids.length == 0){
        bootbox.alert('请先选择要删除的应用');
        return;
    }

    bootbox.confirm("Are you sure?", function (result) {
        if(result) {
            $.ajax({
                url: '/project/project_myos_delete',
                type: 'post',
                data: {project_id: project_id, app_ids: app_ids.join(',')},
                success: function () {
                    window.location.reload();
                }
            });
        }
    });
}<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""

from . import department

from flask.ext.login import login_user, logout_user, login_required, current_user
from flask import render_template, redirect, url_for, request, flash, current_app, session, send_file


from tms.project.androidProjectModules import *
from tms.auth.models import User, Role
from tms.auth.models import *
from tms.departmentMan.models import *
import json

DEPARTMENT_DEEP = 6

@department.route('/department_manage')
@login_required
def department_manage():
    """
    部门管理
    :return:
    """

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '部门管理', 'url': url_for('department.department_manage')})
    session['menu_path'] = ['部门管理', '部门管理']

    #一级部门
    departments_1_list = Department.query.filter_by(level=1).order_by(Department.id.asc()).all()
    result = {}

    for department in departments_1_list:
        #一级部门

        #获取一级部门下面的子节点(部门)
        array = search_childs(department)

        department.leader_name = get_leader_name(department.leader)

        result[department] = array

    return render_template('departmentMan/department_manage_page.html', parents=departments_1_list, menus=result)


def search_childs(department1):
    """获取一级部门下面的子节点(部门)"""

    #查找二级部门
    departments2 = Department.query.filter_by(level=department1.level+1,parent_id=department1.id).order_by(Department.id.asc()).all()

    array = []

    for department2 in departments2:

        dict = {}
        dict['id'] = department2.id
        dict['name'] = department2.name
        dict['activate'] = department2.activate
        dict['note'] = department2.note
        dict['order'] = department2.order

        leader_name = get_leader_name(department2.leader)

        dict['leader_name'] = leader_name

        dict['childs'] = search_childs(department2)

        array.append(dict)

    return array


def get_leader_name(leader):
    """获取leader名称"""

    if leader:

        leader_array = str(leader).split(',')

        if len(leader_array) == 0:
            return ''

        leader_name_array = []

        for arr in leader_array:

            user = User.query.filter_by(id=arr).first()

            leader_name_array.append(user.display_name)

        return ','.join(leader_name_array)

    else:

        return ''



@department.route('/department_activate')
@login_required
def set_department_activate():

    department_id = request.args.get('id', 0, type=int)
    activate = request.args.get('activate')

    if activate == 'Enable':
        db.session.execute('update tms_department SET activate = 1 WHERE id in ( SELECT aa.id from ( SELECT id from tms_department t where FIND_IN_SET(id,getChildsByRootid(%s))) aa )' % (department_id))
    elif activate == 'Disable':
        db.session.execute('update tms_department SET activate = 0 WHERE id in ( SELECT aa.id from ( SELECT id from tms_department t where FIND_IN_SET(id,getChildsByRootid(%s))) aa )' % (department_id))

    db.session.commit()

    return '{"result": true}'


@department.route('/edit_department/<int:department_id>',  methods=['GET', 'POST'])
@login_required
def edit_department(department_id):
    """修改部门"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '编辑部门' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:

        session['breadcrumbs'].append({'text': '编辑部门','url': url_for('department.edit_department',
                                                                     department_id=department_id)})


    department = Department.query.filter_by(id=department_id).first()
    origin_status = department.activate

    if request.method == 'POST':
        #更新数据

        name = request.form['name']
        activate = request.form.get('activate')
        note = request.form.get('note')
        order = request.form['order']

        leader = request.form['leader_ids']
        member = request.form['member_ids']
        parent_department = request.form.get('parent_department', None)

        # parent_id = request.form['parent']

        other_department = Department.query.filter_by(name=str(name).strip()).first()
        if other_department.id != department.id:

            flash(u'同名部门存在', 'danger')
            return redirect(url_for('department.department_manage'))
        # else:
        department.name = name
        department.note = note
        if activate == 'on':
            department.activate = True
        else:
            department.activate = False
        department.order = order
        department.leader = leader

        # if department.parent == None
        #处理
        flag,msg = dealwith_department(department.parent,parent_department,department)

        if not flag:

            flash(msg, 'danger')
            return redirect(url_for('department.department_manage'))

        if parent_department:
            par_department = Department.query.filter_by(id=parent_department).first()

            department.level = par_department.level + 1
            department.parent_id = parent_department
        else:
            department.level = 1
            department.parent_id = None


        #处理状态
        dealwith_status(origin_status,department)

        db.session.add(department)
        db.session.commit()

        #修改成员所在的部门
        before_users = User.query.filter_by(department_id=department.id).all()

        for before_user in before_users:
            before_user.department_id = None

            db.session.add(before_user)

        db.session.commit()

        #修改成员所在的部门信息
        if member:
            member_array = str(member).split(',')

            for arr in member_array:
                if arr :
                    user = User.query.filter_by(id=arr).first()
                    user.department_id = department.id

                    db.session.add(user)

            db.session.commit()

        flash(u'修改成功!', 'info')

        return redirect(url_for('department.department_manage'))
    else:
        #get请求-->展示数据

        # department_list = []
        #
        # selected_department_dict = {}
        #
        # #当前部门所在的level
        # current_level = department.level
        #
        # top_department = getParent(department)
        #
        # if current_level == 1:
        #     """"""
        #
        #     #一级部门
        #     # top_departments = Department.query.filter_by(level=1,activate=1).order_by(Department.id.asc()).all()
        #     #
        #     # department_list.append((1,top_departments))
        #     #
        #     # selected_department_list.append(department.id)
        #
        # else:
        #
        #     #获取父级部门
        #     for i in range(1,current_level):
        #
        #         if i == 1:
        #             departments = Department.query.filter_by(level=i,activate=1).order_by(Department.id.asc()).all()
        #         else:
        #             departments = Department.query.filter_by(level=i,activate=1,parent_id=top_department[i-1]).order_by(Department.id.asc()).all()
        #
        #         department_list.append((i,departments))
        #
        #
        #     #获取父级Id
        #
        #     parent = department.parent
        #
        #     while(current_level > 1):
        #         current_level = current_level - 1
        #
        #         selected_department_dict[current_level] = parent.id
        #
        #         parent = parent.parent

        users = User.query.filter_by(activate=1).all()

        #一级部门
        top_departments = Department.query.filter_by(level=1,activate=1).order_by(Department.id.asc()).all()
        #二级部门
        second_departments = Department.query.filter_by(level=2,activate=1).order_by(Department.id.asc()).all()
        #三级部门
        three_departments = Department.query.filter_by(level=3,activate=1).order_by(Department.id.asc()).all()
        #四级部门
        four_departments = Department.query.filter_by(level=4,activate=1).order_by(Department.id.asc()).all()
        #五级部门
        five_departments = Department.query.filter_by(level=5,activate=1).order_by(Department.id.asc()).all()

        array = []
        array.append('一级部门')
        array.append('二级部门')
        array.append('三级部门')
        array.append('四级部门')
        array.append('五级部门')

        dict = {}
        dict['一级部门'] = top_departments
        dict['二级部门'] = second_departments
        dict['三级部门'] = three_departments
        dict['四级部门'] = four_departments
        dict['五级部门'] = five_departments


        array_leader = department.leader.split(',')

        department_members = User.query.filter_by(department_id=department.id).all()

        member_page = []

        for arr in department_members:
            member_page.append(str(arr.id))

        member_page = ','.join(member_page)

        if len(department_members) == 0 :
            member_page = ''

        return render_template('departmentMan/department_edit.html', department=department,users=users,array=array,dict=dict,array_leader=array_leader,
                               member_page=member_page)


def dealwith_status(origin_status,department):
    """"""

    if department.activate == origin_status:
        #如果状态一致，没有发生变更

        """"""

    else:
        #状态发生变更

        if department.activate:
            # print 'True'
            db.session.execute('update tms_department SET activate = 1 WHERE id in ( SELECT aa.id from '
                               '( SELECT id from tms_department t where FIND_IN_SET(id,getChildsByRootid(%s))) aa )' % (department.id))

        else:
            # print 'False'
            db.session.execute('update tms_department SET activate = 0 WHERE id in ( SELECT aa.id from '
                               '( SELECT id from tms_department t where FIND_IN_SET(id,getChildsByRootid(%s))) aa )' % (department.id))

        db.session.commit()




def dealwith_department(origin_parent_department,page_parent_department,current_department):
    """"""

    if page_parent_department and origin_parent_department:
        #页面传过来的父级部门有值

        if int(page_parent_department) == origin_parent_department.id:
            #页面传过来的父级部门 等于 数据库里的父级部门  --》没有发生变更

            return True,''

        else:

            if pageParentIfInChilds(page_parent_department,current_department):
                #页面选择的父级部门是该部门或者该部门的子级别部门报错

                return False,'页面选择的父级部门不能是该部门或者该部门的子级别部门,请重新选择'

            page_parent_department_entity = Department.query.filter_by(id=page_parent_department).first()

            if page_parent_department_entity.level == origin_parent_department.level:
                #页面传过来的父级部门 级别 与 数据库里的父级部门的级别一致

                return True,''

            elif page_parent_department_entity.level < origin_parent_department.level:
                #页面选择的父级级别 比 数据库的父界部门级别 小

                #修改层级结构

                update_childs(current_department,page_parent_department_entity.level,origin_parent_department.level)


                return True,''

            elif page_parent_department_entity.level > origin_parent_department.level:
                #页面选择的父级部门的级别 大于 数据库中的父级部门级别
                # 4 > 1

                max_level = select_childs(current_department) #6

                diff  = max_level - origin_parent_department.level

                if page_parent_department_entity.level + diff > DEPARTMENT_DEEP:

                    return False,'该操作会导致部门层次级别超过6级,请联系系统管理员'
                else:
                    #修改层级结构
                    #页面选择的父级部门的级别 大于 数据库中的父级部门级别
                    # 3 > 2 当前：3

                    update_childs(current_department,page_parent_department_entity.level,origin_parent_department.level)

                    return True,''

    else:

        if origin_parent_department:
            #页面传过来的父级部门没有值  --> 当前部门设置为一级部门

            update_childs(current_department,0,origin_parent_department.level)

            return True,''

        elif page_parent_department:
            #页面传过来的父级部门有值  --> 数据库里的父级部门没有值  --

            if pageParentIfInChilds(page_parent_department,current_department):
                #页面选择的父级部门是该部门或者该部门的子级别部门报错

                return False,'页面选择的父级部门不能是该部门或者该部门的子级别部门,请重新选择'

            page_parent_department_entity = Department.query.filter_by(id=page_parent_department).first()

            max_level = select_childs(current_department) #6

            diff  = max_level - 0

            if page_parent_department_entity.level + diff > DEPARTMENT_DEEP:

                return False,'该操作会导致部门层次级别超过6级,请联系系统管理员'

            update_childs(current_department,page_parent_department_entity.level,0)

            return True,''

        else:
            #都没有父级部门

            return True,''




def select_childs(current_department):
    """"""

    sql = 'SELECT MAX(t.level) from tms_department t where FIND_IN_SET(id,getChildsByRootid(%s))' % (current_department.id)

    all_levels = db.session.execute(sql).fetchall()

    max_level = all_levels[0][0]

    return max_level


def pageParentIfInChilds(page_parent_department,current_department):

    sql = ' SELECT t.id from tms_department t where FIND_IN_SET(id,getChildsByRootid(%s)) ' % (current_department.id)
    all_departments = db.session.execute(sql).fetchall()

    for department in all_departments:

        if department[0] == int(page_parent_department):

            return True

    return False


def update_childs(current_department,page_parent_level,db_parent_level):
    """
    page_parent_level: 1
    db_parent_level:3
    current_department.level:4

    update_childs(current_department,3,2)

    page_parent_level：3
    db_parent_level：2
    current_department.level:3

    update_childs(current_department,0,2)

    page_parent_level：0
    db_parent_level：2
    current_department.level:3
    """

    diff = int(page_parent_level) - db_parent_level#-2

    db.session.execute('update tms_department SET level = level + %s  WHERE id in '
                       '( SELECT aa.id from ( SELECT id from tms_department t where FIND_IN_SET(id,getChildsByRootid(%s))) aa )' % (diff,current_department.id))

    db.session.commit()






def getParent(department):
    """获取父级节点"""

    dict = {}

    while(department):
        dict[department.level] = department.id
        department = department.parent

    return dict



@department.route('/add_department', methods=['GET', 'POST'])
@login_required
def add_department():
    """新增部门"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '新增部门' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:
        session['breadcrumbs'].append({'text': '新增部门',
                                       'url': url_for('department.add_department')})

    if request.method == 'POST':
        #新增部门到数据库

        name = request.form['name']
        activate = request.form.get('activate')
        note = request.form.get('note')
        order = request.form['order']

        leader = request.form['leader_ids']
        member = request.form['member_ids']
        parent_department = request.form.get('parent_department', None)

        # print 'leader',leader
        # print 'parent_department',parent_department
        # #一级部门
        # select_department_1 = request.form.get('select_department_1', None)
        # #二级部门
        # select_department_2 = request.form.get('select_department_2', None)
        # #三级部门
        # select_department_3 = request.form.get('select_department_3', None)
        # #四级部门
        # select_department_4 = request.form.get('select_department_4', None)
        # #五级部门
        # select_department_5 = request.form.get('select_department_5', None)

        #最多深度到6

        other_department = Department.query.filter_by(name=str(name).strip()).first()
        if other_department:

            flash(u'同名部门存在', 'danger')
            return redirect(url_for('department.department_manage'))
        # else:

        department = Department()

        department.name = name
        department.note = note
        if activate == 'on':
            department.activate = True
        else:
            department.activate = False
        department.order = order
        department.leader = leader

        if parent_department:
            par_department = Department.query.filter_by(id=parent_department).first()

            department.level = par_department.level + 1
            department.parent_id = parent_department
        else:
            department.level = 1
            department.parent_id = None


        # if select_department_1:
        #     #一级部门有值
        #
        #     department.level = 2
        #     department.parent_id = select_department_1
        #
        #     if select_department_2:
        #         #二级部门有值
        #         department.level = 3
        #         department.parent_id = select_department_2
        #
        #         if select_department_3:
        #             #三级部门有值
        #             department.level = 4
        #             department.parent_id = select_department_3
        #
        #             if select_department_4:
        #                 #四级部门有值
        #                 department.level = 5
        #                 department.parent_id = select_department_4
        #
        #                 if select_department_5:
        #                     #五级部门有值
        #                     department.level = 6
        #                     department.parent_id = select_department_5
        #
        # else:
        #     #一级部门没有值
        #     #该部门即为一级部门
        #
        #     department.level = 1

        db.session.add(department)
        db.session.commit()

        #修改成员所在的部门信息
        if member:
            member_array = str(member).split(',')

            for arr in member_array:
                if arr :
                    user = User.query.filter_by(id=arr).first()
                    user.department_id = department.id

                    db.session.add(user)

            db.session.commit()


        flash(u'新增成功!', 'info')

        return redirect(url_for('department.department_manage'))

    else:
        #get请求 -- >进入新增部门页面

        users = User.query.filter_by(activate=1).all()

        #一级部门
        top_departments = Department.query.filter_by(level=1,activate=1).order_by(Department.id.asc()).all()
        #二级部门
        second_departments = Department.query.filter_by(level=2,activate=1).order_by(Department.id.asc()).all()
        #三级部门
        three_departments = Department.query.filter_by(level=3,activate=1).order_by(Department.id.asc()).all()
        #四级部门
        four_departments = Department.query.filter_by(level=4,activate=1).order_by(Department.id.asc()).all()
        #五级部门
        five_departments = Department.query.filter_by(level=5,activate=1).order_by(Department.id.asc()).all()

        array = []
        array.append('一级部门')
        array.append('二级部门')
        array.append('三级部门')
        array.append('四级部门')
        array.append('五级部门')

        dict = {}
        dict['一级部门'] =  top_departments
        dict['二级部门'] =  second_departments
        dict['三级部门'] =  three_departments
        dict['四级部门'] =  four_departments
        dict['五级部门'] =  five_departments

        return render_template('departmentMan/department_add.html', department=None,top_departments=top_departments,users=users,
                               array=array,dict=dict)



@department.route('/department_childs', methods=['GET', 'POST'])
@login_required
def department_childs():
    """部门-->子部门"""

    department_id = request.form.get('id')
    level = request.form.get('level')
    level = int(level) + 1

    departments = Department.query.filter_by(parent_id=department_id,level=level,activate=1).order_by(Department.id.asc()).all()

    department_list = []

    for department in departments:

        dict = {}
        dict['id'] = department.id
        dict['name'] = department.name

        department_list.append(dict)


    return json.dumps(department_list)<file_sep># -*- coding:utf-8 -*-
__author__ = 'changjie.fan'

""""""
import json
from datetime import datetime

from flask import render_template, redirect, url_for, request, flash, current_app, session
from flask.ext.login import login_required, current_user

from . import qa
from tms.project.models import Project, ProjectTestCase, Version, VersionHistory, SpecialVersion
from tms.auth.models import User, Role
from models import *
from tms.qa.views import need_test_version


@qa.route('/my_project')
@login_required
@need_test_version()
def my_project():
    """测试负责人时我的项目"""

    session['breadcrumbs'] = [session['breadcrumbs'][0]]
    session['breadcrumbs'].append({'text': '我的项目', 'url': url_for('qa.my_project')})
    session['menu_path'] = ['项目管理', '我的项目']

    projects = Project.query.filter(Project.test_leader_id == current_user.id).all()

    return render_template('qa/project_page.html', projects=projects)


@qa.route('/all_project')
@login_required
@need_test_version()
def all_project():
    """所有的项目项目"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '所有项目' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:
        session['breadcrumbs'].append({'text': '所有项目', 'url': url_for('qa.all_project')})

    projects = Project.query.all()

    return render_template('qa/project_page.html', projects=projects,all_project=True)


@qa.route('/project_test_case_page/<int:project_id>', methods=['GET', 'POST'])
@login_required
@need_test_version()
def project_test_case_page(project_id):
    """项目测试用例管理"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '项目用例管理' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:
        session['breadcrumbs'].append({'text': '项目用例管理',
                                       'url': url_for('qa.project_test_case_page', project_id=project_id)})

    project = Project.query.filter(Project.id == project_id).first()
    if request.method == 'POST':
        test_case_ids = request.form.getlist('test_case_ids')
        for case_id in test_case_ids:
            project_case = ProjectTestCase()
            project_case.project = project
            project_case.test_case_id = int(case_id)

            db.session.add(project_case)
        db.session.commit()

        project_test_cases = ProjectTestCase.query.filter(ProjectTestCase.project_id == project_id).all()
        project_test_case_ids = [tmp.test_case.id for tmp in project_test_cases]
        all_test_case = TestCase.query.filter(
            db.and_(TestCase.activate == True,
                    TestCase.id.notin_(project_test_case_ids))).all()
        flash(u'添加成功', 'info')
        return redirect(url_for('qa.project_test_case_page', project_id=project_id))

    else:
        project_test_cases = ProjectTestCase.query.filter(ProjectTestCase.project_id == project_id).all()
        project_test_case_ids = [tmp.test_case.id for tmp in project_test_cases]
        all_test_case = TestCase.query.filter(
            db.and_(TestCase.activate == True,
                    TestCase.id.notin_(project_test_case_ids))).all()

        return render_template('qa/project_test_case_page.html',
                               project=project,
                               all_test_case=all_test_case,
                               project_test_cases=project_test_cases)


@qa.route('/delete_project_case', methods=['POST'])
@login_required
def delete_project_case():
    """删除项目指定的用例"""

    project_case_ids = request.form.get('project_case_ids', '').split(',')

    project_case = ProjectTestCase.query.filter(ProjectTestCase.id.in_(project_case_ids)).all()
    for case in project_case:
        db.session.delete(case)
    db.session.commit()

    flash(u'删除成功', 'info')

    return '{"result": true}'


@qa.route('/edit_project/<int:project_id>', methods=['GET', 'POST'])
@login_required
@need_test_version()
def edit_project(project_id):
    """编辑"""

    for index, breadcrumb in enumerate(session['breadcrumbs']):
        if '编辑项目' == breadcrumb['text']:
            session['breadcrumbs'] = session['breadcrumbs'][:index+1]
            break
    else:
        session['breadcrumbs'].append({'text': '编辑项目',
                                       'url': url_for('developer.edit_project', project_id=project_id)})

    project_obj = Project.query.filter_by(id=project_id).first()

    if request.method == 'POST':
        project_key = request.form['project_key']
        test_leader_id = request.form.get('test_leader', '')
        status = request.form['status']
        to_email = request.form.get('to_email', '')
        test_case_jar_url = request.form.get('test_case_jar_url', '')

        project_obj.project_key = project_key
        project_obj.test_case_jar_url = test_case_jar_url
        project_obj.test_leader_id = test_leader_id
        project_obj.status = status
        project_obj.to_email = ';'.join([tmp.strip() for tmp in to_email.strip().split(';') if tmp.strip()])

        db.session.add(project_obj)
        db.session.commit()

        flash(u'修改成功!', 'info')
        return redirect(url_for('qa.my_project'))

    # 只有是项目开发负责才能编辑项目信息
    is_developer_leader = False
    if project_obj.develop_leader == current_user:
        is_developer_leader = True

    # 超级管理员和配置管理员也可以编辑项目信息且能够编辑仓库信息
    is_super_man = False
    if session['user']['role_name'] == 'admin' or session['user']['role_name'] == 'scm':
        is_super_man = True


    test_users = User.query.join(User.role).filter(Role.name == 'os_test').all()

    # 项目最新的版本
    last_version = Version.query.filter(db.and_(Version.project_id == project_obj.id,
                                                Version.version_number == project_obj.last_version_number)).first()
    last_version_id = ''
    if last_version:
        last_version_id = last_version.id

    # 项目的发布对象(版本对象)
    special_versions = SpecialVersion.query.filter(SpecialVersion.project_id == project_obj.id).all()

    # 项目状态
    project_category = ['MTK', 'QCM', 'RDA', 'APP']
    # 项目类
    project_status = ['开发中', '迭代发布中', '已关闭', '已暂停']

    return render_template('/qa/project_edit.html',
                           project=project_obj,
                           last_version_id=last_version_id,
                           special_versions=special_versions,
                           is_super_man=is_super_man,
                           is_developer_leader=is_developer_leader,
                           test_users=test_users,
                           project_status=project_status,
                           project_category=project_category)
