FasterRunner (httptunner)搭建以及功能补充(邮件、修改定时任务)

配置

下载地址
https://github.com/httprunner/FasterRunner


后端配置
https://www.jianshu.com/p/e26ccc21ddf2

前端配置
https://www.cnblogs.com/luopan/p/10250485.html

mac下安装RabbitMq
下载brew: https://blog.csdn.net/iteapoy/article/details/134701525
下载RabbitMq:https://www.cnblogs.com/yihuihui/p/9095130.html
网络慢,本地下载RabbitMq:https://blog.csdn.net/a116385895/article/details/111938495
入口
http://localhost:8080/fastrunner/login

问题:
1. pycharm 创建的虚拟环境不能pip
https://www.jianshu.com/p/e46e36addf8d
2. pymysql/mysqlclient caching_sha2_password,
https://blog.csdn.net/weekdawn/article/details/81039382

django 1.8.2 mysql 加密方式 caching_sha2_password
django 2.0.3 mysql 加密方式 mysql_native_password

部分功能配置及补充

定时任务配置

1. setting.py
djcelery.setup_loader()
CELERY_ENABLE_UTC = True
CELERY_TIMEZONE = 'Asia/Shanghai'
# BROKER_URL = 'amqp://username:password@IP:5672//'
BROKER_URL = 'amqp://guest:guest@127.0.0.1:5672//'
CELERYBEAT_SCHEDULER = 'djcelery.schedulers.DatabaseScheduler'
CELERY_RESULT_BACKEND = 'djcelery.backends.database:DatabaseBackend'
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'

CELERY_TASK_RESULT_EXPIRES = 7200
CELERYD_CONCURRENCY = 1 if DEBUG else 5
CELERYD_MAX_TASKS_PER_CHILD = 40
2.定时服务
cd /home/conan/conan-ta/FasterRunner/
nohup python3 manage.py celery beat -l info >> /Users/zd/Documents/FasterRunner/logs/beat.log 2>&1 &


cd /home/conan/conan-ta/FasterRunner/
celery multi start w1 -A FasterRunner -l info --logfile=/Users/zd/Documents/FasterRunner/logs/worker.log 2>&1 &

3.调试定时任务
更改定时相关逻辑时,需要关掉并重新启动celery beat,celery multi 才会生效,打印出来的调试信息在logs/worker.log下查看。
举例:定时任务,增加发送邮件的标题:

fastrunner/utils/task.py

 

 

fastrunner/task.py

 

 

 

 

发送邮件

setting.py

# 发邮件
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_SEND_USERNAME = 'no-reply@fenbi.com'  # 定时任务报告发送邮箱,支持163,qq,sina,企业qq邮箱等,注意需要开通smtp服务
EMAIL_SEND_PASSWORD = ''     # 邮箱密码
EMAIL_PORT = 25
EMAIL_USE_TLS = True

 

fastrunner/utils/email.py

import smtplib
from email.mime.text import MIMEText
from email.header import Header
from FasterRunner.settings import EMAIL_SEND_USERNAME, EMAIL_SEND_PASSWORD



def send_email_reports(receiver,save_summary,Cc=None,title=None):
    receiver = receiver.rstrip(';')
    all_receivers = receiver.split(';')
    if '@sina.com' in EMAIL_SEND_USERNAME:
        smtpserver = 'smtp.sina.com'
    elif '@163.com' in EMAIL_SEND_USERNAME:
        smtpserver = 'smtp.163.com'
    else:
        smtpserver = 'smtp.exmail.qq.com'

    if title:
        subject = "【%s】接口自动化测试报告"%title
    else:
        subject = "接口自动化测试报告"
    smtp = smtplib.SMTP_SSL(smtpserver, 465)
    smtp.login(EMAIL_SEND_USERNAME, EMAIL_SEND_PASSWORD)
    msg = MIMEText(save_summary, "html", "utf-8")
    msg["Subject"] = Header(subject, "utf-8")
    msg['From'] = Header('no-reply', 'utf-8')
    msg['To'] = receiver
    # 处理抄送
    if Cc:
        Cc = Cc.rstrip(';')
        msg['Cc'] = Cc
        all_receivers = receiver + ';' + Cc
        all_receivers = all_receivers.split(';')

    smtp.sendmail(EMAIL_SEND_USERNAME, all_receivers, msg.as_string())

 

fastrunner/tasks.py

  1 from fastrunner.utils.emails import send_email_reports
  2 import time
  3 
  4 @shared_task
  5 def schedule_debug_suite(*args, **kwargs):
  6     """定时任务
  7     """
  8     print("定时任务start.....")
  9     project = kwargs["project"]
 10     receiver = kwargs["receiver"]
 11     Cc = kwargs["copy"]
 12     title = kwargs["name"]
 13 
 14     print("receiver****:%s"%receiver)
 15     print("args****:%s"%args)
 16     print("kwargs****:%s"%kwargs)
 17     print("定时任务end.....")
 18     receiver = receiver.strip()
 19     Cc = Cc.strip()
 20 
 21     suite = []
 22     test_sets = []
 23     config_list = []
 24     for pk in args:
 25         try:
 26             name = models.Case.objects.get(id=pk).name
 27             suite.append({
 28                 "name": name,
 29                 "id": pk
 30             })
 31         except ObjectDoesNotExist:
 32             pass
 33 
 34     for content in suite:
 35         test_list = models.CaseStep.objects. \
 36             filter(case__id=content["id"]).order_by("step").values("body")
 37 
 38         testcase_list = []
 39         config = None
 40         for content in test_list:
 41             body = eval(content["body"])
 42             if "base_url" in body["request"].keys():
 43                 config = eval(models.Config.objects.get(name=body["name"], project__id=project).body)
 44                 continue
 45             testcase_list.append(body)
 46         config_list.append(config)
 47         test_sets.append(testcase_list)
 48 
 49     summary = debug_suite(test_sets, project, suite, config_list, save=False)
 50     save_summary("", summary, project, type=3)
 51 
 52     # 整理数据
 53     testTime = summary['time']['start_at']
 54     testTime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(testTime))
 55     durTime = str(summary['time']['duration'])[:8]
 56 
 57     totalApi = summary['stat']['testsRun']
 58     successApi = summary['stat']['successes']
 59     FailApi = summary['stat']['failures']
 60     errorApi = summary['stat']['errors']
 61     skipApi = summary['stat']['skipped']
 62 
 63     htmll = """
 64     <table border="1" cellpadding="0" cellspacing="0" width="700px">
 65         <tr style="background-color: #f8f8fa">
 66             <th>测试时间</th>
 67             <th>持续时间</th>
 68             <th>Total</th>
 69             <th>Success</th>
 70             <th>Failed</th>
 71             <th>Error</th>
 72             <th>Skipped</th>
 73         </tr>
 74         <tr>
 75             <td>%s</td>
 76             <td>%s 秒</td>
 77             <td>%s</td>
 78             <td>%s</td>
 79             <td>%s</td>
 80             <td>%s</td>
 81             <td>%s</td>
 82         </tr>
 83     </table>
 84     <div style="height: 30px"></div>
 85     <table border="1" cellpadding="0" cellspacing="0" width="700px">
 86         <tr style="background-color: #f8f8fa">
 87             <th>名称</th>
 88             <th>请求地址</th>
 89             <th>请求方法</th>
 90             <th>响应时间(ms)</th>
 91             <th>测试结果</th>
 92         </tr>
 93     """ % (
 94         testTime, durTime, totalApi, successApi, FailApi, errorApi, skipApi
 95     )
 96 
 97     # 名称/请求地址/请求方法/响应时间/测试结果
 98     for i in summary['details']:  # [{},{}]
 99         detail = i['records']  # 列表
100         for d in detail:
101             name = d['name']
102             uurl = d['meta_data']['request']['url']
103             method = d['meta_data']['request']['method']
104             responseTime = d['meta_data']['response']['response_time_ms']
105             iresult = d['status']
106 
107             htmll += """
108             <tr>
109             <td>%s</td>
110             <td>%s</td>
111             <td>%s</td>
112             <td>%s (ms)</td>
113             <td>%s</td>
114          </tr>
115             """ % (name, uurl, method, responseTime, iresult)
116 
117     htmll = htmll + '</table>'
118 
119 
120 
121     if Cc:
122         send_email_reports(receiver, htmll, Cc=Cc,title=title)
123     else:
124         send_email_reports(receiver, htmll,title=title)
View Code

 

 

编辑定时任务

前端 Task.vue

1、父传子,将编辑页面的数据,跨页面传给AddTask.vue

  1 <template>
  2 
  3     <el-container>
  4         <el-header style="background: #fff; padding: 0; height: 50px">
  5             <div class="nav-api-header">
  6                 <div style="padding-top: 10px; margin-left: 20px">
  7                     <el-button                  
  8                         type="primary"
  9                         size="small"
 10                         icon="el-icon-circle-plus-outline"
 11                         @click="addTasks2()"
 12                     >添加任务
 13                     </el-button>
 14 
 15                     <el-button
 16                         :disabled="!addTasks"
 17                         type="text"
 18                         style="position: absolute; right: 30px;"
 19                         @click="addTasks=false"
 20               
 21                     >返回列表
 22                     </el-button>
 23 
 24                 </div>
 25             </div>
 26         </el-header>
 27 
 28         <el-container>
 29             <el-header v-if="!addTasks" style="padding: 0; height: 50px; margin-top: 10px">
 30                 <div style="padding-top: 8px; padding-left: 30px;">
 31                     <el-pagination
 32                         :page-size="11"
 33                         v-show="tasksData.count !== 0 "
 34                         background
 35                         @current-change="handleCurrentChange"
 36                         :current-page.sync="currentPage"
 37                         layout="total, prev, pager, next, jumper"
 38                         :total="tasksData.count"
 39                     >
 40                     </el-pagination>
 41                 </div>
 42             </el-header>
 43             <el-main style="padding: 0; margin-left: 10px; margin-top: 10px;">
 44                 <div style="position: fixed; bottom: 0; right:0; left: 230px; top: 160px">
 45                     <el-table
 46                         v-if="!addTasks"
 47                         :data="tasksData.results"
 48                         :show-header="tasksData.results.length !== 0 "
 49                         stripe
 50                         highlight-current-row
 51                         height="calc(100%)"
 52                         @cell-mouse-enter="cellMouseEnter"
 53                         @cell-mouse-leave="cellMouseLeave"
 54                     >
 55 
 56                         <el-table-column
 57                             label="任务名称"
 58                             width="240"
 59                         >
 60                             <template slot-scope="scope">
 61                                 <div>{{scope.row.name}}</div>
 62                             </template>
 63                         </el-table-column>
 64 
 65 
 66                         <el-table-column
 67                             width="120"
 68                             label="时间配置"
 69                         >
 70                             <template slot-scope="scope">
 71                                 <div>{{scope.row.kwargs.corntab}}</div>
 72                             </template>
 73                         </el-table-column>
 74 
 75                         <el-table-column
 76                             width="100"
 77                             label="邮件策略"
 78                         >
 79                             <template slot-scope="scope">
 80                                 <div>{{scope.row.kwargs.strategy}}</div>
 81 
 82                             </template>
 83                         </el-table-column>
 84 
 85 
 86                         <el-table-column
 87                             width="80"
 88                             label="状态"
 89                         >
 90                             <template slot-scope="scope">
 91                                 <el-switch
 92                                     disabled
 93                                     v-model="scope.row.enabled"
 94                                     active-color="#13ce66"
 95                                     inactive-color="#ff4949">
 96                                 </el-switch>
 97                             </template>
 98                         </el-table-column>
 99                         <el-table-column
100                             width="320"
101                             label="接收人"
102                         >
103                             <template slot-scope="scope">
104                                 <div>{{scope.row.kwargs.receiver}}</div>
105                             </template>
106                         </el-table-column>
107                         <el-table-column
108                             width="320"
109                             label="抄送人"
110                         >
111                             <template slot-scope="scope">
112                                 <div>{{scope.row.kwargs.copy}}</div>
113                             </template>
114                         </el-table-column>
115 
116                         <el-table-column
117                             width="200"
118                         >
119                             <template slot-scope="scope">
120                                 <el-row v-show="currentRow === scope.row">
121                                     <el-button
122                                         type="info"
123                                         icon="el-icon-edit"
124                                         circle size="mini"
125                                         @click="editTask(scope.row)"
126                                     ></el-button>
127                                     <el-button
128                                         type="danger"
129                                         icon="el-icon-delete"
130                                         circle size="mini"
131                                         @click="delTasks(scope.row.id)"
132                                     >
133                                     </el-button>
134                                 </el-row>
135                             </template>
136 
137                         </el-table-column>
138 
139                     </el-table>
140                 </div>
141             </el-main>
142             <!-- 将本页面的编辑数据,传给AddTask.vue -->
143             <AddTasks
144                 v-if="addTasks"
145                 v-on:changeStatus="changeStatus"
146                 :editValue=this.editValue
147             >
148             </AddTasks>
149             
150         </el-container>
151     </el-container>
152 
153 </template>
154 
155 <script>
156     import AddTasks from './AddTasks'
157 
158     export default {
159         components: {
160             AddTasks
161         },
162         data() {
163             return {
164                 editValue:[],            
165                 addTasks: false,
166                 currentPage: 1,
167                 currentRow: '',
168                 tasksData: {
169                     count: 0,
170                     results: []
171                 },
172             }
173         },
174         methods: {
175             // 添加任务,要把编辑的内容置空
176             addTasks2(){
177                 this.addTasks = true   
178                 this.editValue = []
179             },
180             editTask(val){
181                 console.log(val)
182                 this.addTasks = true    
183                 this.editValue = val // 父传子       
184                 // this.$store.commit("dispalyTask", val); 缓存方式
185                 // this.addTasks.ruleForm.name = this.$store.state.task.name;
186       
187             },
188 
189             delTasks(id) {
190                 this.$confirm('此操作将永久删除该定时任务,是否继续?', '提示', {
191                     confirmButtonText: '确定',
192                     cancelButtonText: '取消',
193                     type: 'warning',
194                 }).then(() => {
195                     this.$api.deleteTasks(id).then(resp => {
196                         if (resp.success) {
197                             this.getTaskList();
198                         }
199                     })
200                 })
201             },
202             handleCurrentChange(val) {
203                 this.$api.getTaskPaginationBypage({
204                     params: {
205                         page: this.currentPage,
206                         project: this.$route.params.id
207                     }
208                 }).then(resp => {
209                     this.tasksData = resp;
210                 })
211             },
212 
213             changeStatus(value) {
214                 this.getTaskList();
215                 this.addTasks = value;
216             },
217             getTaskList() {
218                 this.$api.taskList({params: {project: this.$route.params.id}}).then(resp => {
219                     this.tasksData = resp
220                 })
221             },
222             cellMouseEnter(row) {
223                 this.currentRow = row;
224             },
225 
226             cellMouseLeave(row) {
227                 this.currentRow = '';
228             },
229         },
230         name: "Tasks",
231         mounted() {
232             this.getTaskList();
233         }
234     }
235 </script>
236 
237 <style>
238 
239 
240 </style>
View Code

 

前端 AddTasks.vue

1、编辑数据回显

2、在原来保存任务的接口上延伸出更新,将修改的数据传后端

  1 <template>
  2     <el-container>
  3         <template v-if="!next">
  4             <el-main style="padding-top: 0">
  5                 <div style="margin-top: 10px;">
  6                     <el-col :span="12">
  7                         <el-form
  8                             :model="ruleForm"
  9                             :rules="rules"
 10                             ref="ruleForm"
 11                             label-width="100px"
 12                         >
 13                             <el-form-item label="任务名称" prop="name" >
 14                                
 15                                 <el-input  v-model="ruleForm.name" placeholder="请输入任务名称" clearable=""></el-input>
 16                             </el-form-item>
 17 
 18                             <el-form-item label="时间配置" prop="corntab">
 19                                 <el-input clearable v-model="ruleForm.corntab" placeholder="请输入cortab表达式,例如 2 12 * * *"></el-input>
 20                             </el-form-item>
 21 
 22                             <el-form-item label="任务状态" prop="switch">
 23                                 <el-switch v-model="ruleForm.switch"></el-switch>
 24                             </el-form-item>
 25 
 26                             <el-form-item label="邮件策略" prop="strategy">
 27                                 <el-radio-group v-model="ruleForm.strategy">
 28                                     <el-radio label="始终发送"></el-radio>
 29                                     <el-radio label="仅失败发送"></el-radio>
 30                                     <el-radio label="从不发送"></el-radio>
 31                                 </el-radio-group>
 32                             </el-form-item>
 33 
 34                             <el-form-item label="邮件接收人列表" prop="receiver">
 35                                 <el-input type="textarea" v-model="ruleForm.receiver"
 36                                           placeholder="多个接收人以;分隔" clearable></el-input>
 37                             </el-form-item>
 38 
 39                             <el-form-item label="邮件抄送人列表" prop="copy">
 40                                 <el-input type="textarea" v-model="ruleForm.copy"
 41                                           placeholder="多个抄送人以;分隔" clearable></el-input>
 42                             </el-form-item>
 43 
 44                             <el-form-item>
 45                                 <el-button type="primary" @click="submitForm('ruleForm')">下一步</el-button>
 46                                 <el-button @click="resetForm('ruleForm')">重置</el-button>
 47                             </el-form-item>
 48                         </el-form>
 49 
 50                     </el-col>
 51 
 52                 </div>
 53             </el-main>
 54         </template>
 55         <template v-if="next">
 56             <el-aside style="margin-top: 10px;">
 57                 <div class="nav-api-side">
 58                     <div class="api-tree">
 59                         <el-input
 60                             placeholder="输入关键字进行过滤"
 61                             v-model="filterText"
 62                             size="medium"
 63                             clearable
 64                             prefix-icon="el-icon-search"
 65                         >
 66                         </el-input>
 67                         <el-tree
 68                             @node-click="handleNodeClick"
 69                             :data="dataTree"
 70                             node-key="id"
 71                             :default-expand-all="false"
 72                             :expand-on-click-node="false"
 73                             highlight-current
 74                             :filter-node-method="filterNode"
 75                             ref="tree2"
 76                         >
 77                             <span class="custom-tree-node"
 78                                   slot-scope="{ node, data }"
 79                             >
 80                                 <span><i class="iconfont" v-html="expand"></i>&nbsp;&nbsp;{{ node.label }}</span>
 81                             </span>
 82                         </el-tree>
 83                     </div>
 84 
 85                 </div>
 86 
 87             </el-aside>
 88             <el-main style="padding-top: 0px">
 89                 <div>
 90                     <el-row :gutter="20">
 91                         <el-col :span="12">
 92                             <el-pagination
 93                                 :page-size="11"
 94                                 v-show="suiteData.count !== 0"
 95                                 background
 96                                 @current-change="handlePageChange"
 97                                 :current-page.sync="currentPage"
 98                                 layout="total, prev, pager, next, jumper"
 99                                 :total="suiteData.count"
100                                 style="text-align: center"
101                             >
102                             </el-pagination>
103                         </el-col>
104                         <el-col :span="12">
105                             <el-button type="primary" v-if="testData.length > 0" @click="saveTask">保存</el-button>                           
106                             <el-button v-if="testData.length > 0" @click="next=false">上一步</el-button>
107                         </el-col>
108 
109 
110                     </el-row>
111                 </div>
112 
113                 <div>
114                     <el-row :gutter="20">
115                         <el-col :span="12">
116                             <div 
117                                 v-for="(item,index) in suiteData.results"
118                                 draggable='true'
119                                 @dragstart="currentSuite = JSON.parse(JSON.stringify(item))"
120                                 style="cursor: pointer; margin-top: 10px; overflow: auto;"
121                                 :key="index"
122                               
123 
124                             >
125                             
126 
127                                 <div class="block block_options"  >
128                                     <span class="block-method block_method_options block_method_color">Case</span>
129                                     <span class="block_name">{{item.name}}</span>
130                                 </div>
131                             </div>
132                         </el-col>
133                         <el-col :span="12">
134 
135                             <div style="max-height: 600px; overflow: auto"
136                                  @drop='drop($event)'
137                                  @dragover='allowDrop($event)'
138                             >
139                                 <span
140                                     v-if="testData.length ===0"
141                                     style="color: red">温馨提示:<br/>选择左侧相应用例节点显示可拖拽的用例<br/>从左边拖拽用例至此区域组成任务列表<br/>
142                                     上下拖动此区域任务调整监控调用顺序
143                                 </span>
144 
145                                 
146 
147                                 <div class='test-list'>
148                                     <draggable
149                                         v-model="testData"
150                                         @end="dragEnd"
151                                         @start="length = testData.length"
152                                         :options="{animation:200}"
153                                     >
154                                         <div
155                                             v-for="(test, index) in testData"
156                                             :key="index"
157                                             class="block block_test"
158                                             @mousemove="currentTest = index"
159                                         >
160                                         <span
161                                             class="block-method block_method_test block_method_color">Tasks</span>
162                                             <span class="block-test-name">{{test.name}}</span>
163 
164                                             <el-button
165                                                 style="position: absolute; right: 12px; top: 8px"
166                                                 v-show="currentTest === index"
167                                                 type="danger"
168                                                 icon="el-icon-delete"
169                                                 circle size="mini"
170                                                 @click="testData.splice(index, 1)"
171                                             >
172                                            
173                                             </el-button>
174                                         </div>
175                                     </draggable>
176                                 </div>
177 
178                             </div>
179                         </el-col>
180                     </el-row>
181                 </div>
182             </el-main>
183         </template>
184     </el-container>
185 
186 
187 </template>
188 
189 <script>
190     import draggable from 'vuedraggable'
191 
192     export default {
193 
194         name: "AddTasks",
195         watch: {
196             filterText(val) {
197                 this.$refs.tree2.filter(val);
198             },
199         },
200         components: {
201             draggable
202         },
203         props:['editValue'],
204         
205         data() {
206             return {
207          
208                 currentTest: '',
209                 length: 0,
210                 testData: [],
211                 currentSuite: '',
212                 search: '',
213                 next: false,
214                 node: '',
215                 currentPage: 1,
216                 filterText: '',
217                 expand: '&#xe65f;',
218                 dataTree: [],
219                 suiteData: {
220                     count: 0,
221                     results: []
222                 },
223                 ruleForm: {
224                     name: '',
225                     switch: true,
226                     corntab: '',
227                     strategy: '始终发送',
228                     receiver: '',
229                     copy: ''
230                 },
231                 rules: {
232                     name: [
233                         {required: true, message: '请输入任务名称', trigger: 'blur'},
234                         {min: 1, max: 50, message: '长度在 1 到 50 个字符', trigger: 'blur'}
235                     ],
236                     corntab: [
237                         {required: true, message: '请输入正确的corntab表达式', trigger: 'blur'}
238                     ]
239 
240                 }
241             }
242         },
243         methods: {
244             // 将之前的保存任务扩展成保存+修改。判断逻辑,如果拿到了父传子的数据(编辑数据),点击保存就走更新接口。
245             saveTask(){
246                 // var task = [];
247                 // for(let value of this.testData){
248                 //     task.push(value.id);
249                 // }
250                 // var form = this.ruleForm;
251                 // form["data"] = task ;
252                 // form["project"] = this.$route.params.id;
253 
254                 if(this.editValue.kwargs){
255                     var task = [];
256                     for(let value of this.testData){
257                         task.push(value.id);
258                     }
259                     var form = this.ruleForm;
260                     form["data"] = task ;
261                     form["project"] = this.$route.params.id;
262                     form["taskId"] = this.editValue.id;
263 
264 
265 
266                     console.log("更新任务。。。")
267                     this.$api.updateTask11(form).then(resp => {
268                     if(!resp.success){
269                         this.$message.error(resp.msg)
270                     }else{
271                         this.$emit("changeStatus", false);
272                     }
273                 })
274                 }else{
275                     var task2 = [];
276                     for(let value2 of this.testData){
277                         task2.push(value2.id);
278                     }
279                     var form2 = this.ruleForm;
280                     form2["data"] = task2 ;
281                     form2["project"] = this.$route.params.id;
282                     
283                     this.$api.addTask(form2).then(resp => {
284                     if(!resp.success){
285                         this.$message.error(resp.msg)
286                     }else{
287                         this.$emit("changeStatus", false);
288                     }
289                 })
290             }
291 
292                 
293             },
294             dragEnd(event) {
295                 if (this.testData.length > this.length) {
296                     this.testData.splice(this.length, 1)
297                 }
298             },
299             drop(event) {
300                 event.preventDefault();
301                 this.testData.push(this.currentSuite);
302             
303             },
304             allowDrop(event) {
305                 event.preventDefault();
306             },
307             handlePageChange(val) {
308                 this.$api.getTestPaginationBypage({
309                     params: {
310                         page: this.currentPage,
311                         node: this.node,
312                         project: this.$route.params.id,
313                         search: ''
314                     }
315                 }).then(res => {
316                     this.suiteData = res;
317                 })
318             },
319             handleCurrentChange(val) {
320                 this.$api.getTestPaginationBypage({
321                     params: {
322                         page: this.currentPage,
323                         project: this.$route.params.id,
324                         node: this.node,
325                         search: this.search
326                     }
327                 }).then(resp => {
328                     this.suiteData = resp;
329                 })
330             },
331             // 点击下一步,将testData 回显出来
332             submitForm(formName) {
333                 this.$refs[formName].validate((valid) => {
334                     if (valid) {
335                         this.next = true;
336                         
337                         // var a = []
338                         // var b = []
339                         console.log("带出之前编辑的项目2");
340                         console.log(this.suiteData);
341                         console.log(this.testData);
342                         for(var i=0;i<this.suiteData.results.length;i++){
343                             console.log("ok1");
344                             // a.push(this.suiteData.results[i])
345                             for(var i2=0;i2<this.editValue.args.length;i2++){
346                                 
347                                 if(this.suiteData.results[i].id==this.editValue.args[i2]){
348                                     this.testData.push(this.suiteData.results[i])       
349                                     // b.push(this.suiteData.results[i])
350                                 }
351                                 
352                             }
353                             
354                         };
355 
356                       
357                      // 差集,把用过的case去掉,展示在左侧
358                         // var minus = a.filter(v => !b.includes(v));
359                         // this.suiteData.results=minus
360                        
361 
362                     } else {
363                         return false;
364                     }
365                 });
366             },
367             resetForm(formName) {
368                 this.$refs[formName].resetFields();
369             },
370             filterNode(value, data) {
371                 if (!value) return true;
372                 return data.label.indexOf(value) !== -1;
373             },
374             getTree() {
375                 this.$api.getTree(this.$route.params.id, {params: {type: 2}}).then(resp => {
376                     this.dataTree = resp.tree;
377                 })
378             },
379 
380             handleNodeClick(node) {
381                 this.node = node.id;
382                 this.getTestList();
383 
384             },
385             getTestList() {
386                 this.$api.testList({
387                     params: {
388                         project: this.$route.params.id,
389                         node: this.node,
390                         search: this.search
391                     }
392                 }).then(resp => {
393                     this.suiteData = resp;
394                 })
395             },
396 
397             // 父传子,从Task.vue 传来的数据
398             getEditData(){
399             console.log("****")
400             console.log(this.editValue)
401             // console.log(this.editValue.name)
402             console.log(this.editValue.args)
403             if(this.editValue.kwargs){
404                 this.ruleForm.name=this.editValue.name;
405                 this.ruleForm.corntab=this.editValue.kwargs.corntab;
406                 this.ruleForm.receiver=this.editValue.kwargs.receiver;
407                 this.ruleForm.copy=this.editValue.kwargs.copy;
408                 this.ruleForm.strategy=this.editValue.kwargs.strategy;
409             }
410             
411                     
412             },
413         },
414        
415         mounted() {
416             this.getTree();
417             this.getTestList();
418             this.getEditData(); //自动加载编辑数据
419             
420         }
421     }
422 </script>
423 
424 <style scoped>
425 
426     .test-list {
427         height: 590px;
428     }
429 
430     .block_test {
431         margin-top: 10px;
432         border: 1px solid #49cc90;
433         background-color: rgba(236, 248, 238, .4)
434     }
435 
436     .block_method_test {
437         background-color: #304056;
438     }
439 
440     .block-test-name {
441         width: 700px;
442         padding-left: 10px;
443         -webkit-box-flex: 1;
444         -ms-flex: 1;
445         font-family: Open Sans, sans-serif;
446         color: #3b4151;
447         border: none;
448         outline: none;
449         background: rgba(236, 248, 238, .4)
450 
451     }
452 
453 
454 </style>
View Code

 

前端 api.js

更新接口

1 export const updateTask11 = params => {
2     return axios.patch('/api/fastrunner/schedule/', params).then(res => res.data)
3 };

 

后端 schedule.py 

1、更新定时任务,涉及到了三只表

2、修改了删除,把关联的时间也删除

 1  @method_decorator(request_log(level='INFO'))
 2     def update(self, request):
 3         """
 4         编辑项目
 5         """
 6         print("**编辑定时任务")
 7         project = request.data["project"]
 8 
 9         try:
10             project2 = models2.Project.objects.get(id=project)
11 
12         except (KeyError, ObjectDoesNotExist):
13             return Response(response.SYSTEM_ERROR)
14 
15 
16         project2.name = request.data['name']
17         project2.save()
18         print("项目名称保存完成!")
19         print("-------")
20         print("这是一堆什么东西?", request.data)
21 
22         task2 = Task(**request.data)
23         resp = task2.update_task()
24         print("**5: ", resp)
25         print("**6: ", request.data)
26 
27         return Response(response.PROJECT_UPDATE_SUCCESS)
28 
29 
30 
31     @method_decorator(request_log(level='INFO'))
32     def delete(self, request, **kwargs):
33         """删除任务
34         """
35         print("**删除")
36         task = models.PeriodicTask.objects.get(id=kwargs["pk"])
37         task.enabled = False
38         task.delete()
39         schedule = models.CrontabSchedule.objects.get(id=task.crontab_id)
40         schedule.delete()
41         return Response(response.TASK_DEL_SUCCESS)
View Code

 

后端 task.py

  1 import json
  2 import logging
  3 from djcelery import models as celery_models
  4 
  5 from fastrunner.utils import response
  6 from fastrunner.utils.parser import format_json
  7 
  8 logger = logging.getLogger('FasterRunner')
  9 
 10 
 11 class Task(object):
 12     """
 13     定时任务操作
 14     """
 15 
 16     def __init__(self, **kwargs):
 17         logger.info("before process task data:\n {kwargs}".format(kwargs=format_json(kwargs)))
 18         self.__name = kwargs["name"]
 19         self.__data = kwargs["data"]
 20         self.__corntab = kwargs["corntab"]
 21         self.__switch = kwargs["switch"]
 22         self.__task = "fastrunner.tasks.schedule_debug_suite"
 23         self.__project = kwargs["project"]
 24         self.__email = {
 25             "strategy": kwargs["strategy"],
 26             "copy": kwargs["copy"],
 27             "receiver": kwargs["receiver"],
 28             "corntab": self.__corntab,
 29             "project": self.__project
 30         }
 31 
 32         self.__corntab_time = None
 33         self.__taskId = ""
 34         # 编辑的时候需要
 35         for i in kwargs.keys():
 36             if "taskId" in i:
 37                 self.__taskId = kwargs["taskId"]
 38         print("邮件**", self.__email)
 39 
 40     def format_corntab(self):
 41         """
 42         格式化时间
 43         """
 44         corntab = self.__corntab.split(' ')
 45         if len(corntab) > 5:
 46             return response.TASK_TIME_ILLEGAL
 47         try:
 48             self.__corntab_time = {
 49                 'day_of_week': corntab[4],
 50                 'month_of_year': corntab[3],
 51                 'day_of_month': corntab[2],
 52                 'hour': corntab[1],
 53                 'minute': corntab[0]
 54             }
 55         except Exception:
 56             return response.TASK_TIME_ILLEGAL
 57 
 58         return response.TASK_ADD_SUCCESS
 59 
 60     def add_task(self):
 61         """
 62         add tasks
 63         """
 64         if celery_models.PeriodicTask.objects.filter(name__exact=self.__name).count() > 0:
 65             logger.info("{name} tasks exist".format(name=self.__name))
 66             return response.TASK_HAS_EXISTS
 67 
 68         if self.__email["strategy"] == '始终发送' or self.__email["strategy"] == '仅失败发送':
 69             if self.__email["receiver"] == '':
 70                 return response.TASK_EMAIL_ILLEGAL
 71 
 72         resp = self.format_corntab()
 73         if resp["success"]:
 74             task, created = celery_models.PeriodicTask.objects.get_or_create(name=self.__name, task=self.__task)
 75             crontab = celery_models.CrontabSchedule.objects.filter(**self.__corntab_time).first()
 76             if crontab is None:
 77                 crontab = celery_models.CrontabSchedule.objects.create(**self.__corntab_time)
 78             task.crontab = crontab
 79             task.enabled = self.__switch
 80             task.args = json.dumps(self.__data, ensure_ascii=False)
 81             task.kwargs = json.dumps(self.__email, ensure_ascii=False)
 82             task.description = self.__project
 83             task.save()
 84             logger.info("{name} tasks save success".format(name=self.__name))
 85             return response.TASK_ADD_SUCCESS
 86         else:
 87             return resp
 88 
 89     def update_task(self):
 90         """
 91         update tasks
 92         """
 93         print("update tasks......")
 94         print(self)
 95         try:
 96             if self.__email["strategy"] == '始终发送' or self.__email["strategy"] == '仅失败发送':
 97                 if self.__email["receiver"] == '':
 98                     return response.TASK_EMAIL_ILLEGAL
 99 
100             obj = celery_models.PeriodicTask.objects.filter(id=self.__taskId).first()
101             print("--8",obj,self.__name)
102             obj.name = self.__name
103             obj.enabled = self.__switch
104             obj.args = json.dumps(self.__data, ensure_ascii=False)
105             obj.kwargs = json.dumps(self.__email, ensure_ascii=False)
106             obj.description = self.__project
107 
108             obj.save()
109             print("--9",obj.crontab_id)
110             obj2 = celery_models.CrontabSchedule.objects.filter(id=obj.crontab_id).first()
111 
112             timeSplit = self.__corntab.split(' ')
113 
114             obj2.minute = timeSplit[0]
115             obj2.hour = timeSplit[1]
116             obj2.day_of_month = timeSplit[2]
117             obj2.month_of_year = timeSplit[3]
118             obj2.day_of_week = timeSplit[4]
119             obj2.save()
120 
121             return response.TASK_UPDATE_SUCCESS
122         except Exception as e:
123             return "定时任务更新失败,原因:{%s}"%e
View Code

 

启动/关闭 shell

start.sh

#!/bin/bash

#启动 FasterWeb
echo -e "启动 FasterWeb"
cd /home/conan/conan-ta/FasterWeb/
nohup npm run build >> /home/shared/log/npm.log 2>&1 &


# 启动 FasterRunner
echo -e "启动 FasterRunner"
cd /home/conan/conan-ta/FasterRunner/
nohup python3 manage.py runserver 0.0.0.0:9000 >> /home/shared/log/django.log 2>&1 &


# 使用默认的celery.py启动
echo -e "启动celery beat"
cd /home/conan/conan-ta/FasterRunner/
nohup python3 manage.py celery beat -l info >> /Users/zd/Documents/FasterRunner/logs/beat.log 2>&1 &


# 使用默认的celery.py启动
echo -e "启动 celery work"
cd /home/conan/conan-ta/FasterRunner/
celery multi start w1 -A FasterRunner -l info --logfile=/Users/zd/Documents/FasterRunner/logs/worker.log 2>&1 &

stop.sh

#!/bin/bash

# kill django pid
echo -e "shutting down django pid"
pids=$(ps aux | grep "python" | grep "runserver" | awk '{print $2}')
for pid in $pids
do
        kill -9 $pid
done


# kill celery beat pid
echo -e "shutting down celery beat pid"
pids=$(ps aux | grep "celery" | grep "FasterRunner" | awk '{print $2}')
for pid in $pids
do
        kill -9 $pid
done
posted @ 2019-12-15 00:19  东方不败--Never  阅读(1583)  评论(0编辑  收藏  举报