zoukankan      html  css  js  c++  java
  • 20200320 代码发布之完结

    * 任务单展示页
    
      不是直接书写增删改查,由于任务肯定是给项目使用的,所以做成了项目的一个展示字段,点击先查看当前项目所有的任务单记录,之后渲染添加按钮完成添加功能
    
      这么做的目的在于添加任务的时候无需选择项目,
    
    * 任务单添加页
    
      一开始我们也是公用项目与服务器表的添加页面
    
      并且添加页涉及到前端标签渲染,数据校验,信息展示所以借助于modelform完成
    
      并不是模型表中所有的字段都需要展示到前端,exclude剔除(uid,project,status)
    
      后台重写save方法,将uid和project手动传值
    
      
    
      对任务单的添加页单独开设html文件书写
    
      该页面有三块区域
    
      * 当前任务对应的项目基本信息展示
    
      * 基本配置
    
      * 脚本钩子
    
        注意脚本钩子的个数是不固定的,根据不同的业务逻辑设计不同个数的钩子
    
        前端样式代码拷贝书写
    
    
    
    ​	针对钩子界面需要额外的渲染select选择框、checkbox、文本框
    
    ​	
    
    ​	钩子模版的保存脚本功能
    
    * 发布任务界面简单搭建
    
      websocket、gojs、paramiko、gitpython
    
      知识简单的实现了,gojs数据取决于后端
    
    
    
    * 数据的群发功能
    
    
    

    代码发布

    • 发布任务
    • 节点动态展示
    • 内部执行流程

    群发功能

    我们之前也实现过群聊的功能,但是我们那种是非主流的,不推荐使用

    如果你想要完美的实现群发功能,channels也给你提供了相关的模块

    channels-layers模块

    基础配置

    • 配置文件中配置

      # settings中配置
      
      # channel-layers配置
      CHANNEL_LAYERS = {
          'default': {
              'BACKEND':'channels.layers.InMemoryChannelLayer'
          }
      }
      

    使用

    用户再连接的时候就应该分配到不同的群号

    • 将用户添加到群聊中group_add()
            # 将当前的用户加入到群聊中 (括号内子一个参数是群号,第二个是用户的唯一表示)
            async_to_sync(self.channel_layer.group_add(task_id,self.channel_layer))
    
    
    • 给特定群号里的用户发送数据group_send()
    # 给特定群号里面的用户发送数据
    	async_to_sync(self.channel_layer.group_send)('123',{'type':'my.send','message':{'code':'init','data':node_list}})
    
    '''
    type参数指定的是发送数据的方法
    	my.send  > > > 自定义一个my_send 方法
    	xxx.ooo  > > > 自定义一个xxx_ooo方法
    	
    	message参数后指定的是发送的数据
    	
    将message后面的数据交给type指定的方法发送给用户
    '''
    
        def mysend(self,event):
            '''发送数据功能
            async_to_sync 会循环调用该方法,给群聊这里面的每一个用户发送数据
            for obj in [obj1,obj2,obj3]:
                obj.send()
            '''
            # 获取message后面的数据 
            message = event.get('message')
            # 直接发送
            self.send(json.dumps(message))
    
    
    • 获取url中携带的无名或有名分组 self.scope
    # self.scope 看成一个大字典,字典中有前端的所有信息
    task_id = self.scope['url_route']['kwargs'].get('task_id')
    # task_id = self.scope['url_route']['args'].get('task_id')  无名分组
    
    • 用户断开连接,剔除用户 group_disacad
        def websocket_disconnect(self, message):
            # 当前用户断开连接之后,应该提出群聊
            task_id = self.scope['url_route']['kwargs'].get('task_id')
            # 去群里将用户剔除
            async_to_sync(self.channel_layer.group_disacad)(task_id,self.channel_name)
            raise StopConsumer
    

    节点展示

    每一个任务都有自己的对应节点,并且发布之后的任务,在查看的时候应该默认展示之前已经发布了的节点状态

    所以根据节点数据应该单独开设一张表来存储

    节点表的创建

    img

    class Node(models.Model):
        # 一个任务单有多个节点
        task = models.ForeignKey(verbose_name='发布任务单',to='DeployTask')
    
        text = models.CharField(verbose_name='节点文字',max_length=32)
    
        # 节点颜色 初始化颜色 成功之后的颜色  失败之后的颜色
        status_choices = [
            ('lightgray','待发布'),
            ('green','成功'),
            ('red','失败'),
        ]
        status = models.CharField(verbose_name='状态',max_length=32,choices=status_choices,default='lightgray')
    
        # 自关联  根节点 子节点
        parent = models.ForeignKey(verbose_name='父节点',to='self',null=True,blank=True)
    
        # 节点与服务器 一对多
        servers = models.ForeignKey(to='Server',verbose_name='服务器',null=True,blank=True)
    

    动态创建节点信息

    根据数据库的信息进行数据的展示

    • 操作数据库
    • 构造gojs所需要的节点数据
      • 先做基本的节点: 开始,下载,打包上传,服务器
      • 钩子节点的创建

    优化

    • 当图表已将创建了点击初始化图表按钮不应该再创建
    • 打开一个任务页面的时候如果之前已经创建过了,应该直接渲染出来
    • 将创建节点数据和构造gojs 数据的代码封装成函数 (拆分解耦合)

    先做基本的节点

    先做基本的节点,不靠考虑钩子节点
    开始,下载,打包上传,服务器
    

    代码

        def websocket_receive(self, message):
            text = message.get('text')
            if text == 'init':
                # node_list = [
                #     {"key": "start", "text": '开始', "figure": 'Ellipse', "color": "lightgreen"},
                #     {"key": "download", "parent": 'start', "text": '下载代码', "color": "lightgreen", "link_text": '执行中...'},
                #     {"key": "compile", "parent": 'download', "text": '本地编译', "color": "lightgreen"},
                #     {"key": "compile", "parent": 'download', "text": '本地编译', "color": "lightgreen"},
                #     {"key": "compile", "parent": 'download', "text": '本地编译', "color": "lightgreen"},
                # ]
                task_id = self.scope['url_route']['kwargs'].get('task_id')
                # 动态创建节点信息
                '''
                先做基本的节点,不靠考虑钩子节点
                开始,下载,打包上传,服务器
                '''
    
                # 添加节点对象到列表中
                node_object_list = []
                # 判断当前任务是否已经创建过图表数据
                node_query = models.Node.objects.filter(task_id=task_id)
    
                if not node_query: # 进行创建
                    # 1.先创建节点
                    start_node = models.Node.objects.create(text='开始',task_id=task_id)
                    node_object_list.append(start_node)
    
                    # 下载节点
                    down_load_node = models.Node.objects.create(text='下载',task_id=task_id,parent=start_node)
                    node_object_list.append(down_load_node)
    
                    # 上传节点
                    upload_node = models.Node.objects.create(text='上传',task_id=task_id,parent=down_load_node)
                    node_object_list.append(upload_node)
    
                    # 服务器节点需要考虑服务器的个数
                    task_obj = models.DeployTask.objects.filter(pk=task_id).first()
                    # 跨表查询获得所有的服务器对象
                    for server_obj in task_obj.project.servers.all():
                        server_node = models.Node.objects.create(text=server_obj.hostname,
                                                                 task_id=task_id,
                                                                 parent=upload_node,
                                                                 servers=server_obj
                                                                 )
                        node_object_list.append(server_node)
    
                else: # 有数据直接使用数据库的
                    node_object_list = node_query
    
                # 2.构造gojs需要的节点数据
                node_list = []
                for node_object in node_object_list:
                    temp = {
                        'key': str(node_object.pk),
                        'text': node_object.text,
                        'color': node_object.status
                    }
                    # 判断当前节点对象是否有父节点,如果有则需要再添加一对键值对 parent
                    if node_object.parent:
                        temp['parent'] = str(node_object.parent_id)
                    # 添加到列表中
                    node_list.append(temp)   # [{}, {}, {}]
    
                # 单独给当前连接对象发送数据
                # self.send(text_data=json.dumps({"code":'init','data':node_list}))
    
                # 给特定群号里面的用户发送数据
                async_to_sync(self.channel_layer.group_send)('123',{'type':'my.send','message':{'code':'init','data':node_list}})
                '''
                type参数指定的是发送数据的方法
                	my.send  > > > 自定义一个my_send 方法
                	xxx.ooo  > > > 自定义一个xxx_ooo方法
    
                	message参数后指定的是发送的数据
    
                将message后面的数据交给type指定的方法发送给用户
                '''
    
    

    判断当前任务是否已经初始化节点数据,直接返回

        def websocket_connect(self, message):
            self.accept()
            # 获取url中携带的无名或有名分组参数
            # self.scope 看成一个大字典,字典中有前端的所有信息
            task_id = self.scope['url_route']['kwargs'].get('task_id')
            # task_id = self.scope['url_route']['args'].get('task_id')  无名分组
    
            # 将当前的用户加入到群聊中 (括号内子一个参数是群号,第二个是用户的唯一表示)
            async_to_sync(self.channel_layer.group_add(task_id,self.channel_layer))
    
    
            #优化: 查询当前任务是否已经初始化节点数据,如果有就直接返回给前端展示
            node_queryset = models.Node.objects.filter(task_id=task_id)
            if node_queryset:
                node_list = []
                for node_object in node_queryset:
                    temp = {
                        'key': str(node_object.pk),
                        'text': node_object.text,
                        'color': node_object.status
                    }
                    # 判断当前节点对象是否有父节点,如果有则需要再添加一对键值对 parent
                    if node_object.parent:
                        temp['parent'] = str(node_object.parent_id)
                    # 添加到列表中
                    node_list.append(temp)  # [{}, {}, {}]
    
                # 发送数据给前端  (单发,谁刷新页面给谁发送)
                self.send(text_data=json.dumps({"code":'init','data':node_list}))
    
    

    钩子节点的创作

    就是判断当前任务对象是否有钩子脚本内容数据

    img

    if not node_query: # 进行创建
        # 1.先创建节点
        start_node = models.Node.objects.create(text='开始',task_id=task_id)
        node_object_list.append(start_node)
    
        # 判断用户是否填写了下载前钩子
        if task_obj.before_download_script:
            # 有 则需要创建节点,并利用变量名指向,将start_node由开始节点指向下载前钩子节点
            start_node = models.Node.objects.create(text='下载前',task_id=task_id,parent=start_node)
            node_object_list.append(start_node)
    

    封装成函数

    from channels.generic.websocket import WebsocketConsumer
    from channels.exceptions import StopConsumer
    import json
    from asgiref.sync import async_to_sync
    from app01 import models
    
    
    def create_node(task_obj,task_id):
        # 判断当前任务是否已经创建过图表数据
        node_object_list = models.Node.objects.filter(task_id=task_id)
    
        if node_object_list:
            return node_object_list
    
        if not node_object_list:  # 进行创建
            node_object_list = []
    
            # 1.先创建节点
            start_node = models.Node.objects.create(text='开始', task_id=task_id)
            node_object_list.append(start_node)
    
            # 判断用户是否填写了下载前钩子
            if task_obj.before_download_script:
                # 有 则需要创建节点,并利用变量名指向,将start_node由开始节点指向下载前钩子节点
                start_node = models.Node.objects.create(text='下载前', task_id=task_id, parent=start_node)
                node_object_list.append(start_node)
    
            # 下载节点
            down_load_node = models.Node.objects.create(text='下载', task_id=task_id, parent=start_node)
            node_object_list.append(down_load_node)
    
            # 同理 下载后节点的创建也是如此
            # 判断用户是否填写了下载前钩子
            if task_obj.after_download_script:
                # 有 则需要创建节点,并利用变量名指向,将start_node由开始节点指向下载前钩子节点
                down_load_node = models.Node.objects.create(text='下载后', task_id=task_id, parent=down_load_node)
                node_object_list.append(down_load_node)
    
            # 上传节点
            upload_node = models.Node.objects.create(text='上传', task_id=task_id, parent=down_load_node)
            node_object_list.append(upload_node)
    
            # 服务器节点需要考虑服务器的个数
            task_obj = models.DeployTask.objects.filter(pk=task_id).first()
            # 跨表查询获得所有的服务器对象
            for server_obj in task_obj.project.servers.all():
                server_node = models.Node.objects.create(text=server_obj.hostname,
                                                         task_id=task_id,
                                                         parent=upload_node,
                                                         servers=server_obj
                                                         )
                node_object_list.append(server_node)
    
                # 判断发布前钩子
                if task_obj.before_deploy_script:
                    server_node = models.Node.objects.create(text='发布前',
                                                             task_id=task_id,
                                                             parent=server_node,
                                                             servers=server_obj
                                                             )
                    node_object_list.append(server_node)
    
                # 额外的再添加一个节点 : 发布节点
                deploy_node = models.Node.objects.create(text='发布',
                                                         task_id=task_id,
                                                         parent=server_node,
                                                         servers=server_obj
                                                         )
                node_object_list.append(deploy_node)
    
                # 同理 发布后的钩子
                if task_obj.after_deploy_script:
                    after_deploy_ndoe = models.Node.objects.create(text='发布前',
                                                                   task_id=task_id,
                                                                   parent=deploy_node,
                                                                   servers=server_obj
                                                                   )
                    node_object_list.append(after_deploy_ndoe)
            return node_object_list
    
    
    def convert_object_to_js(node_object_list):
        # 2.构造gojs需要的节点数据
        node_list = []
        for node_object in node_object_list:
            temp = {
                'key': str(node_object.pk),
                'text': node_object.text,
                'color': node_object.status
            }
            # 判断当前节点对象是否有父节点,如果有则需要再添加一对键值对 parent
            if node_object.parent:
                temp['parent'] = str(node_object.parent_id)
            # 添加到列表中
            node_list.append(temp)  # [{}, {}, {}]
    
        return node_list
    
    class PublishConsumer(WebsocketConsumer):
        def websocket_connect(self, message):
            self.accept()
            # 获取url中携带的无名或有名分组参数
            # self.scope 看成一个大字典,字典中有前端的所有信息
            task_id = self.scope['url_route']['kwargs'].get('task_id')
            # task_id = self.scope['url_route']['args'].get('task_id')  无名分组
    
            # 将当前的用户加入到群聊中 (括号内子一个参数是群号,第二个是用户的唯一表示)
            async_to_sync(self.channel_layer.group_add(task_id,self.channel_layer))
    
    
            #优化: 查询当前任务是否已经初始化节点数据,如果有就直接返回给前端展示
            node_queryset = models.Node.objects.filter(task_id=task_id)
            if node_queryset:
                node_list = convert_object_to_js(node_queryset)
                # 发送数据给前端  (单发,谁刷新页面给谁发送)
                self.send(text_data=json.dumps({"code":'init','data':node_list}))
    
        def websocket_receive(self, message):
            text = message.get('text')
            if text == 'init':
                # node_list = [
                #     {"key": "start", "text": '开始', "figure": 'Ellipse', "color": "lightgreen"},
                #     {"key": "download", "parent": 'start', "text": '下载代码', "color": "lightgreen", "link_text": '执行中...'},
                #     {"key": "compile", "parent": 'download', "text": '本地编译', "color": "lightgreen"},
                #     {"key": "compile", "parent": 'download', "text": '本地编译', "color": "lightgreen"},
                #     {"key": "compile", "parent": 'download', "text": '本地编译', "color": "lightgreen"},
                # ]
                task_id = self.scope['url_route']['kwargs'].get('task_id')
                task_obj = models.DeployTask.objects.filter(pk=task_id).first()
    
                # 动态创建节点信息
                '''
                先做基本的节点,不靠考虑钩子节点
                开始,下载,打包上传,服务器
                '''
                # 1.调用create_node()创建节点
                node_object_list= create_node(task_obj, task_id)
    
                # 2. 调用convert_object_to_js() 获取gojs所需数据
                node_list = convert_object_to_js(node_object_list)
    
                # 单独给当前连接对象发送数据
                # self.send(text_data=json.dumps({"code":'init','data':node_list}))
                # 给特定群号里面的用户发送数据
                async_to_sync(self.channel_layer.group_send)('123',{'type':'my.send','message':{'code':'init','data':node_list}})
                '''
                type参数指定的是发送数据的方法
                	my.send  > > > 自定义一个my_send 方法
                	xxx.ooo  > > > 自定义一个xxx_ooo方法
    
                	message参数后指定的是发送的数据
    
                将message后面的数据交给type指定的方法发送给用户
                '''
    
        def mysend(self,event):
            '''发送数据功能
            async_to_sync 会循环调用该方法,给群聊这里面的每一个用户发送数据
            for obj in [obj1,obj2,obj3]:
                obj.send()
            '''
            # 获取message后面的数据
            message = event.get('message')
            # 直接发送
            self.send(json.dumps(message))
    
        def websocket_disconnect(self, message):
            # 当前用户断开连接之后,应该提出群聊
            task_id = self.scope['url_route']['kwargs'].get('task_id')
            # 去群里将用户剔除
            async_to_sync(self.channel_layer.group_disacad)(task_id,self.channel_name)
            raise StopConsumer
    

    执行任务并实时展示

    点击发布任务按钮,执行操作并实时展示状态

    • 先模拟所有的操作都是成功过的
    • 将所有的代码封装成一个方法
      • 如果想要实时展示信息,需要开设线程处理
    • 再真正的执行

    真正的执行代码

    远程仓库的代码保存到本地进行执行

  • 相关阅读:
    NYOJ 625 笨蛋的难题(二)
    NYOJ 102 次方求模
    ZJU Least Common Multiple
    ZJUOJ 1073 Round and Round We Go
    NYOJ 709 异形卵
    HDU 1279 验证角谷猜想
    BNUOJ 1015 信息战(一)——加密程序
    HDU 1202 The calculation of GPA
    "蓝桥杯“基础练习:字母图形
    "蓝桥杯“基础练习:数列特征
  • 原文地址:https://www.cnblogs.com/fwzzz/p/12734004.html
Copyright © 2011-2022 走看看