月度归档: 2019 年 2 月

python常用的几种文件操作,复制,移动,打包

需求

经常会有用到python的文件复制,移动,打包的功能, 我这边用的方法是shuil库来进行这些操作。偶尔穿插了一些图片大小重置pil库和汉字转拼音的方法pypinyin库

代码

# -*- coding: UTF-8 -*-
import os
import shutil
import traceback
import pypinyin
from PIL import Image
from globalLog import ta_log
def copy_file(srcfile, dstfile):
    if not os.path.isfile(srcfile):
        # 一般来说,是因为复制的不是文件所导致的,不影响
        ta_log.info("%s not file!" % (srcfile))
    else:
        fpath, fname = os.path.split(dstfile)  # 分离文件名和路径
        if not os.path.exists(fpath):
            os.makedirs(fpath)  # 创建路径
        shutil.copy2(srcfile, dstfile)  # 移动文件
        ta_log.info("copy %s -> %s" % (srcfile, dstfile))
def movefile(srcfile, dstfile):
    if not os.path.isfile(srcfile):
        ta_log.error("%s not exist!" % (srcfile))
    else:
        fpath, fname = os.path.split(dstfile)  # 分离文件名和路径
        if not os.path.exists(fpath):
            os.makedirs(fpath)  # 创建路径
        shutil.move(srcfile, dstfile)  # 移动文件
        ta_log.info("move %s -> %s" % (srcfile, dstfile))
def copy_file_to_capture(srcfile):
    '''
    复制到capture目录,并且把汉字改成拼音
    :param srcfile:
    :return:
    '''
    fpath, fname = os.path.split(srcfile)
    filename_pre_ = pypinyin.slug(fname)
    dstfile = os.path.abspath(os.getcwd() + "/filepath/attachement/capture/" + filename_pre_)
    copy_file(srcfile, dstfile)
def resize_mage(filein):
    img = Image.open(filein)
    out = img.resize((129, 149), Image.ANTIALIAS)  # resize image with high-quality
    out.save(filein, 'png')
# 只能用于删除本地文件
def delete_many_file(file_list):
    for path in file_list:
        if os.path.exists(path):
            # 删除文件,可使用以下两种方法。
            try:
                os.remove(path)
            except Exception:
                ta_log.error(traceback.format_exc())
            # os.unlink(my_file)
def copy_and_zip(file_list, dst_folder_name):
    '''
    批量复制文件到指定文件夹,然后把指定文件夹的内容压缩成ZIP并且删掉该文件夹
    :param file_list: 文件或文件夹
    :param dst_folder_name: 目标压缩文件的名称
    :return:
    '''
    for item in file_list:
        copy_files_to_attachment(item, dst_folder_name)
    source = os.getcwd() + "/filepath/attachement/" + dst_folder_name
    shutil.make_archive(source, "zip", source)
    shutil.rmtree(source)
def copy_files_to_attachment(srcfile, filename):
    '''
    把文件或文件夹复制到指定目录中
    :param srcfile: 文件或者文件夹的绝对路径
    :param filename: 指定目录
    :return:
    '''
    dstfile = os.path.abspath(os.getcwd() + "/filepath/attachement/")
    folder_name = dstfile + "\\" + filename + "\\"
    if not os.path.isfile(srcfile):
        last_name = os.path.basename(srcfile)
        destination_name = folder_name + last_name
        shutil.copytree(srcfile, destination_name, symlinks=True)
    else:
        fpath, fname = os.path.split(folder_name)  # 分离文件名和路径
        if not os.path.exists(fpath):
            os.makedirs(fpath)  # 创建路径
        shutil.copy2(srcfile, folder_name)  # 移动文件
        print("copy %s -> %s" % (srcfile, folder_name))
def copy_file_to_folder(srcfile, folder_name):
    '''
       把文件或文件夹复制到指定目录中
       :param srcfile: 文件或者文件夹的绝对路径
       :param filename: 指定目录
       :return:
       '''
    if not os.path.isfile(srcfile):
        last_name = os.path.basename(srcfile)
        destination_name = folder_name + last_name
        shutil.copytree(srcfile, destination_name, symlinks=True)
    else:
        fpath, fname = os.path.split(folder_name)  # 分离文件名和路径
        if not os.path.exists(fpath):
            os.makedirs(fpath)  # 创建路径
        shutil.copy2(srcfile, folder_name)  # 移动文件
        print("copy %s -> %s" % (srcfile, folder_name))

python生成bat脚本,并且执行bat脚本

需求

关键词:python,sqllite,bat,进程
最近有一个需求,需要用python读取一个txt文件里的sql语句,用sqllite去执行,执行之前先备份一次sqllite数据库到临时目录。然后sql执行成功则删除临时文件,sql执行失败则使用备份的sqllite去覆盖掉当前错误的sqllite数据库。

关键点

  • 生成临时目录。使用python的tempfile库
  • 复制文件,使用python的shutil库
  • 从文本中读取sql,使用open方法:
  • f = open(file_name, 'r')
    sql = f.read()
  • 生成bat文件,也使用open方法。模式选择w
  • bat命令中的暂停使用@ping -n 5 127.1 >nul 2>nul
  • bat命令中的删除使用“del 路径”
  • bat命令中的复制使用“copy 源文件 目标文件”
  • bat命令执行完后,等待用户手动关闭使用pause,自动关闭使用exit

部分代码

# -*- coding: UTF-8 -*-
# 命名方式为表名_操作_字段
import os
import tempfile
import connectDB
from controller import fileController
curosrdiction = connectDB.curosr_diction
database_back_folder = ""
def exec_sql(sqltext):
    '''
    执行sql,成功返回true
    :param sqltext: sql语句
    :return:
    '''
    result = curosrdiction.executescript(sqltext)
    curosrdiction.commit()
    return result
def backup_database():
    '''
    备份数据库
    :return:
    '''
    global database_back_folder
    database_back_folder = tempfile.mkdtemp()
    fileController.copy_file_to_folder('resource/sqllite.db', database_back_folder)
    write_bat()
def restore_database():
    '''
    恢复数据库
    :return:
    '''
    curosrdiction.close()
    fileController.copy_file(srcfile=database_back_folder + "\\sqllite.db", dstfile='resource/sqllite.db')
def read_sql_from_text(file_name):
    '''
    从文本文件中读取sql并且执行,执行失败,就把原来的数据库覆盖回来
    :param file_name:
    :return:
    '''
    f = open(file_name, 'r')
    sql = f.read()
    if not exec_sql(sql):
        run_bat()
def write_bat():
    sql_database_path = os.getcwd() + "\\resource\\sqllite.db"
    sql_database_path_shm = os.getcwd() + "\\resource\\sqllite.db-shm"
    sql_database_path_wal = os.getcwd() + "\\resource\\sqllite.db-wal"
    bat_name = 'copy.bat'
    s1 = '''@echo off
@ping -n 5 127.1 >nul 2>nul
echo delete database...
del ''' + sql_database_path + '''
del ''' + sql_database_path_shm + '''
del ''' + sql_database_path_wal + '''
echo restore database...
@ping -n 5 127.1 >nul 2>nul
copy ''' + database_back_folder + "\\sqllite.db " + sql_database_path + '''
echo restore finish
pause'''
    f = open(bat_name, 'w')
    f.write(s1)
    f.close()
def run_bat():
    '''
    运行bat
    :return:
    '''
    os.system('start copy.bat')
def update_sql():
    '''
    开始更新数据库的主入口
    :return:
    '''
    backup_database()
    read_sql_from_text("resource/download/1.2.3_sql.txt")

使用jira的api获得project的board参数和sprint参数

需求

每个jira项目都有sprint参数和board参数。关系为一对多的关系。
project:board=1:n
board:sprint=1:n
如果要查询出一个项目有哪些正在进行的sprint,还需要费一番功夫。因为目前jira -api的python库里并没有给出方法,不过我们可以通过下面的方法获得:

通过get请求,根据项目的key或者ID获得board信息,地址和参数如下

url+"/rest/agile/1.0/board?projectKeyOrId=" + projectKeyOrId

通过get请求,根据board的ID查询到sprint的信息,根据state进行筛选

url+"/rest/agile/1.0/board/" + str(item['id']) + "/sprint?state=future,active"

代码

# -- coding: UTF-8 --
import requests
from jira import JIRA
url = 'https://jira.atlassian.com'
jira = JIRA(server=url, basic_auth=('username', 'password'))
cookies = jira._session.cookies
projectKeyOrId = "project_key"
board_url = url+"/rest/agile/1.0/board?projectKeyOrId=" + projectKeyOrId
response = requests.get(url, cookies=cookies,
                        headers={"Accept": "application/json"})
# print(json.dumps(json.loads(response.text), sort_keys=True, indent=4, separators=(",", ": ")))
qq = response.json()
sprint_result_list = []
for item in qq['values']:
    sprint_url = url+"/rest/agile/1.0/board/" + str(item['id']) + "/sprint?state=future,active"
    response = requests.get(url, cookies=cookies,
                            headers={"Accept": "application/json"})
    sprint_json = response.json()
    if 'values' in sprint_json:
        sprint_list = sprint_json['values']
        for sprint in sprint_list:
            element = {'sprint_id': sprint['id'], 'sprint_name': sprint['name']}
            if element not in sprint_result_list:
                sprint_result_list.append(element)
print(sprint_result_list)

VUE+FLASK前后端分离(回答面试题之“你来说说restful前后端代码怎么写”)

动机

前段时间出去面试,遇到了好几次对方面试官问这样的问题,”restful风格的代码,前后端怎么写?”,“从浏览器前端到后台经过了哪些?说的越详细越好。”这样的问题,我听到的第一时刻是懵逼的,啥情况?我要从vue的双向数据绑定开始说吗?axios的用法要说吗?falsk的restful是如何用‘’/api/‘’对应前台的url的吗?还是去说spring框架的mvc? 产生这样的疑惑,主要原因是,我不明白面试官为什么要问这样的问题?实现起来很简单,但是说起来又太宽泛,不知道说的是不是面试官想要的答案,容易偏题。 在我回头仔细想想了之后,决定以后再遇到这样的问题,就使用vue+falsk做例子来讲解这个。

回答策略

按照下面的几步,顺序回答

  1. 以vue+flask 前后端分离为基础,以用户登录,输入用户名密码为场景。
  2. vue前端框架通过v-model获得输入框输入的用户名以及密码。通过引入axios向后台发起http请求,axios是一个http库,可以在nodejs中使用,使用方式有一点类似ajax。通过axios.post(“/api”,{param:”param”})的方式向后台发起http请求。
  3. 后台的flask运行起来之后,通过装饰圈route.配置路由@app.route(‘/api’,methods=[“GET”,”POST”]) 来对应前台http请求的url,如果没有对应的url会返回404。如果找到对应的路由,则会进入相应的方法,进行运算,完成运算之后,可以用json.dumps把数据作为json返回。
  4. axios前台的response收到后,通过response.data获得返回的json,然后可以把相应的值进行变更

代码实现

  • 前端vue关键代码之 index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <title>vueapp01</title>
  <script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script>
</head>
<style>
  .class1{
    background: #444;
    color: #eee;
  }
</style>
<body>
  <div id="app3"></div>
</body>
</html>
  • 前端vue关键代码之main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import Lzw from './components/Lzw'
import router from './router'
import axios from 'axios'
import BootstrapVue from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
Vue.use(BootstrapVue)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
  el: '#app3',
  router,
  axios,
  components: { Lzw },
  template: '<Lzw/>'
})
  • 前端vue关键代码之Lzw.vue

使用axios需要先安装axios库和HTTP2库

npm install –save axios
npm install –save http2

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <div id="example-3">
      <input type="checkbox" id="jack" value="白金会员" v-model="checkedNames">
      <label for="jack">白金会员</label>
      <input type="checkbox" id="john" value="黄金会员" v-model="checkedNames">
      <label for="john">黄金会员</label>
      <input type="checkbox" id="mike" value="王者会员" v-model="checkedNames">
      <label for="mike">王者会员</label>
      <br>
      <strong>选择会员种类: {{ checkedNames }}</strong>
    </div>
    <div id="app2">
      <input v-model="username" placeholder="用户名">
      <p>用户名是: {{ username }}</p>
      <input type="password" v-model="password" placeholder="密码">
      <p>密码是: {{ password }}</p>
      <button class="btn btn-large btn-primary" v-on:click="login">向后台发送post请求,传递用户名和密码,变更用户ID</button>
      <p>用户ID是: {{ id }}</p>
      <button class="btn btn-large btn-primary" v-on:click="getmsg">向后台发送get请求,把用户ID变成0</button>
    </div>
  </div>
</template>
<script>
import axios from "axios";
export default {
  name: "hello",
  data() {
    return {
      msg: "欢迎来到测试开发笔记!",
      checkedNames: [],
      username: "",
      password: "",
      id: "密码反转+用户名反转"
    };
  },
  methods: {
    login() {
      var that = this;
      // 对应 Python 提供的接口,这里的地址填写下面服务器运行的地址,本地则为127.0.0.1,外网则为 your_ip_address
      const path = "http://127.0.0.1:5000/getMsg";
      axios
        .post(path, { username: this.username, password: this.password })
        .then(response => {
          this.id = response.data.userid;
        });
    },
    getmsg() {
      var that = this;
      // 对应 Python 提供的接口,这里的地址填写下面服务器运行的地址,本地则为127.0.0.1,外网则为 your_ip_address
      const path = "http://127.0.0.1:5000/getMsg";
      // 务必使用箭头函数的方法,这样this.id能直接对上,不然会报错提示id没找到
      axios
        .get(path, { username: this.username, password: this.password })
        .then(response => {
          this.id = response.data.userid;
        });
    }
  }
};
</script>
  • 后端flask关键代码main.py

flask要避免跨域问题。需要安装Flask库和Falsk-Cors库

from flask import Flask, url_for,request
from flask_cors import *
import json
app = Flask(__name__)
# 这句话解决跨域问题
CORS(app, supports_credentials=True)
@app.route('/getMsg',methods=["GET","POST"])
def getMsg():
    if request.method == 'POST':
        username = request.json['username']
        password= request.json['password']
        # 假定用户id是密码反转+用户名反转得出来的
        datat = {
            "userid": username[::-1]+password[::-1],
        }
        return json.dumps(datat)
    elif request.method == 'GET':
        datat = {
            "userid": 0,
        }
        return json.dumps(datat)
if __name__ == '__main__':
    app.debug = True
    app.run()

苏ICP备18047533号-1