可视化练习一

4.1新建Flask可视化项目

项目命名为ViewData, 项目中新建文件夹app/models存放数据库映射类,app/static/js存放js的外部工具包,app/templates/main存放html前端页面,app/templates/errors存放404之后的html前端页面,app/views存放SQLAlchemy的语法的数据库访问类。

ViewData/
├── app/
│    ├── models/         # 数据库映射类
│    │        └── __init__.py
│    ├── static/         # 外部工具包
│    │        └── js/
│    ├── templates/        # 模板
│    │        ├── main/        # 存放html前端页面
│    │        └── errors/
│    ├── views/            # SQLAlchemy语法数据库访问类
│    │        └──__init__.py
│    ├── __init__.py
│    ├── config.py
│    └── extensions.py
├── manage.py

4.2新建app/init.py

新建flask的初始化类,自定义函数def create_app(config_name)封装一个方法,专门用于创建Flask实例;自定义函数def config_errorhandler(app)编写访问失败之后的404页面的跳转设置。

`__init__.py`的作用是让一个呈结构化分布(以文件夹形式组织)的代码文件夹变成可以被导入`import`的软件包。
app/__init__.py

# 封装一个方法,专门用于创建Flask实例
def create_app(config_name): # development
    # 创建应用实例
    app = Flask(__name__)
    # 初始化配置
    app.config.from_object(config.get(config_name) or config['default'])
    # 调用初始化函数
    config[config_name].init_app(app)
    # 配置扩展 函数位置 app/ extensions.py
    config_extensions(app)
    # 配置蓝本
    config_blueprint(app)
    # 错误页面定制
    config_errorhandler(app)
    # 返回应用实例
    return app


# 访问失败之后的404页面的跳转设置
def config_errorhandler(app):
    # 如果在蓝本定制,只针对本蓝本的错误有效,
    # 可以使用app_errorhandler定制全局有效的错误显示
    @app.errorhandler(404)
    def page_not_found(e):
        return render_template('errors/404.html')

4.3新建app/config.py

新建flask连接数据库的配置类,将flask框架连接数据库的配置编写成函数,包括数据库的ip、端口、用户名、密码等。

import os

base_dir = os.path.abspath(os.path.dirname(__file__))
# os.path.abspath 返回绝对路径
# os.path.dirname(__file__) 去掉文件名,返回目录
# 通用配置
# 类中的配置项必须是大写的,否则读取配置失败
class Config:
    # 秘钥
    SECRET_KEY = os.environ.get('SECRET_KEY') or '123456'
    # 没有设置secret_key,可能报Must provide secret_key to use csrf错误提醒
    # Session, Cookies以及一些第三方扩展都会用到SECRET_KEY值

    # 有些字符不宜明文写进代码里,比如数据库密码,个人账户密码,如果写进自己本机的环境变量里,程序用的时候通过os.environ.get()取出来就行了。这样开发人员本机测试的时候用的是自己本机的一套密码,生产环境部署的时候,用的是公司的公共账号和密码,这样就能增加安全性。os.environ是一个字典,是环境变量的字典。"SECRET_KEY"是这个字典里的一个键,如果有这个键,返回对应的值,如果没有,则返回none

    # 数据库
    SQLALCHEMY_COMMIT_ON_TEARDOWN = True # 每次请求结束后都会自动提交数据库中的变动。
    SQLALCHEMY_TRACK_MODIFICATIONS = False # 动态追踪修改设置,如未设置只会提示警告, 不建议开启

    # 额外的初始化操作,即使什么内容都没有写,也是有意义的
    @staticmethod
    def init_app(app):
        pass


# 开发环境   语法:mysql+pymysql://用户名:密码@ip:端口/数据库名
class DevelopmentConfig(Config):
    SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:passwd@localhost:3306/visiondata'


# 测试环境
class TestingConfig(Config):
    SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:passwd@localhost:3306/visiondata'


# 生产环境
class ProductionConfig(Config):
    SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:passwd@localhost:3306/visiondata'


# 配置字典, 导入子类配置
config = {
    'development': DevelopmentConfig,
    'testing': TestingConfig,
    'production': ProductionConfig,
    'default': DevelopmentConfig
}

4.4新建app/extensions.py(扩展)

导入flask相关模块,创建SQLAlchemy对象并初始化Bootstrap。自定义函数def config_extensions(app)完成SQLAlchemy和Bootstrap的初始化

# 导入类库
from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
# Flask-Migrate是用于处理SQLAlchemy 数据库迁移的扩展工具。当Model出现变更的时候,通过migrate去管理数据库变更。修改数据库不会直接手动的去修改,而是去修改ORM对应的模型,然后再把模型映射到数据库中。
from flask_moment import Moment
# Flask-Moment 用来处理时间日期等信息。用这个模块主要是考虑到两点,第一是为了让不同时区的用户看到的都是各自时区的实际时间,而不是服务器所在地的时间。第二是对于一些时间间隔的处理,如果要手动处理很麻烦,如果有模块就很好了。

# 创建对象
bootstrap = Bootstrap()
db = SQLAlchemy()
moment = Moment()
migrate = Migrate(db=db) # 第一个参数是Flask的实例,第二个参数是Sqlalchemy数据库实例



# 初始化
def config_extensions(app):
    bootstrap.init_app(app)
    db.init_app(app)
    moment.init_app(app)
    migrate.init_app(app)

4.5新建app/models/表名.py

编写flask的数据库表的映射类,每个表格对应一个文件,映射类命名与表名一致。

# app/models/avg_money_bigdata.py
class AvgMoneyBigData(db.Model):
    __tablename__ = 'avg_money_bigdata'
    id = db.Column(db.Integer, primary_key=True)
    city = db.Column(db.String(255))
    avg_money = db.Column(db.String(255))

# app/models/avg_money_city.py
class AvgMoneyCity(db.Model):
    __tablename__ = "avg_money_city"
    id = db.Column(db.Integer, primary_key=True)
    city = db.Column(db.String(255))
    avg_money = db.Column(db.String(255))

    def __repr__(self):
        return 'Id: %d , City: %s ,Avg_money: %s' %(self.id, self.city, self.avg_money)

# app/models/bigdata_work.py
class BigDataWork(db.Model):
    __tablename__ = 'bigdata_work'
    id = db.Column(db.Integer, primary_key=True)
    job_name = db.Column(db.String(255))
    company_name = db.Column(db.String(255))
    city = db.Column(db.String(255))
    job_require = db.Column(db.Text)
    recruit_number = db.Column(db.String(255))
    money = db.Column(db.String(255))
    skill_require = db.Column(db.String(255))
    release_date = db.Column(db.String(255))
    sex = db.Column(db.String(255))
    company_detail = db.Column(db.String(255))
    education = db.Column(db.String(255))

# app/models/hot_work.py
class HotWork(db.Model):
    __tablename__ = 'hot_work'
    id = db.Column(db.Integer, primary_key=True)
    job_name = db.Column(db.String(255))
    job_number = db.Column(db.Integer)

4.6新建app/models/init.py

编写flask的访问数据库的初始化文件,导入数据库映射表类。

from app.extensions import db

from .hot_work import HotWork
from .bigdata_work import BigDataWork
from .avg_money_city import AvgMoneyCity
from .avg_money_bigdata import AvgMoneyBigData

4.7新建app/static/js

将html前段需要用到的js工具包存放在js文件夹下。

4.8新建ViewData/mamage.py

新建flask启动类,创建flask实例和数据库迁移命令,以及程序的入口。

import os
from app import create_app
from flask_script import Manager, Server
from flask_migrate import MigrateCommand
# migrate的作用就是在数据库字段改变时不用drop表直接做更新操作
# 获取配置
config_name = os.environ.get('FLASK_CONFIG') or 'default'
# os模块获取环境变量的一个方法

# 创建Flask实例
app = create_app(config_name)

# 创建命令行启动控制对象
#三种方法创建命令,即创建Command子类、使用@command修饰符、使用@option修饰符;
manager = Manager(app)
# Command子类必须定义一个run方法
manager.add_command("runserver", Server(use_debugger=True))

# 添加数据库迁移命令
manager.add_command('db', MigrateCommand)

# 启动项目
if __name__ == '__main__':
    # 调用manager.run()启动Manager实例接收命令行中的命令
    manager.run()

4.9新建app/views/init.py

新建flask的蓝本配置初始化类,进行蓝本的配置,自定义函数def config_blueprint(app)以及封装函数完成蓝本的注册。

from .main import main

# 蓝本配置
DEFAULT_BLUEPRINT = (
    # 蓝本,前缀
    (main, ''),
)


# 封装函数,完成蓝本注册
def config_blueprint(app):
    for blueprint, prefix in DEFAULT_BLUEPRINT:
        app.register_blueprint(blueprint, url_prefix=prefix)

4.10新建app/views的main.py

(1)根据数据库表格hotwork,分析并统计招聘数量最多的前十名热门职位,分别使用柱状图表达

数据源:数据库visiondata中hot_work表

在app/views下的main.py添加函数def get_hot_work() ,使用SQLAlchemy语法编写sql的查询语句,查询统计招聘数据前十的热门职位。

(2)请根据指定表中的数据,统计出全国某些城市指定招聘岗位平均工资,通过南丁格尔玫瑰图进行呈现。

数据源:数据库visiondata中avg_money_city和avg_money_bigdata

在app/views下的main.py添加函数def get_avg_money_city()和def get_avg_money_BigData(),使用SQLAlchemy语法编写sql的查询语句,查询两个表中的城市平均工资数据。

(3)分析并统计招聘数量"大数据"相关职位招聘数量,分别使用柱状图表达,同时在网页后台输出相关数据打印语句。

数据源:数据库visiondata中bigdata_work

在app/views下的main.py添加函数def get_big_data_work(),使用SQLAlchemy语法编写sql的查询语句,对相同职位进行数量汇总。

import json
from flask import Blueprint, render_template, jsonify
from app.models import HotWork, BigDataWork, AvgMoneyCity, AvgMoneyBigData
from app.extensions import db
from sqlalchemy import *

main = Blueprint('main', __name__)  # 实例化路由


@main.route('/')
def index():
    return render_template('/main/echarts.html')    # 当url访问 / 直接跳转到主页


@main.route('/index/')      # url访问 /index/ 跳转主页  调用display方法  将查询结果通过render_template()传入html页面
def display():
    rs_ghw = get_hot_work()
    rs_gbdw = get_big_data_work()
    rs_gamc = get_avg_money_city()
    rs_gamb = get_avg_money_BigData()
    return render_template('/main/echarts.html', rs_ghw=rs_ghw, rs_gbdw=rs_gbdw, rs_gamc=rs_gamc, rs_gamb=rs_gamb)


#  统计招聘数量最多的前十热门职位  获取结果:
def get_hot_work():     # 编写查询语句  返回前10条
    # sql: select * from hot_work group by job_name limit 10
    ghw_rs = HotWork.query.order_by(desc('job_number')).limit(10)
    print(ghw_rs)
    return ghw_rs


# 所有城市招聘数据的平均工资     获取结果
def get_avg_money_city():
    # sql: select * from avg_money_city
    gamc_rs = AvgMoneyCity.query.all()
    print('所有城市招聘数据的平均工资     获取结果成功')
    return gamc_rs


# “大数据”相关职位所有城市招聘数据的平均工资       获取结果
def get_avg_money_BigData():
    # sql: select * from avg_money_big_data
    gamb_rs = AvgMoneyBigData.query.all()
    print('“大数据”相关职位所有城市招聘数据的平均工资       获取结果成功')
    return gamb_rs


# 对相同职位进行数量汇总,"大数据"相关职位招聘数量比较
def get_big_data_work():
    # sql: select job_name,count(*) from bigdata_work where job_name like '%大数据%' GROUP BY job_name
    gbdw_rs = db.session.query(BigDataWork.job_name, func.count('*').label('job_count'))\
        .group_by(BigDataWork.job_name).order_by(desc('job_count')).all()
    print('"大数据"相关职位招聘数量:' + str(gbdw_rs))
    return gbdw_rs

4.11新建app/templates/errors/404.html

编写url访问失败之后的提示页面。



4.12新建app/templates/main/echarts.html

编写js获取到flask传过来的数据,通过echarts组件进行结果的展示(包括css样式)。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>招聘信息统计</title>
    <script type="text/javascript" src="/static/js/echarts.js"></script>
    <script type="text/javascript" src="/static/js/jquery.min.js"></script>
    <script type="text/javascript" src="/static/js/js2wordcloud.js"></script>
</head>
<style>
    html , body , .content {
    width:100%;
    height:100%;
    padding: 0;
    margin: 0;
    box-sizing: border-box;
    background-color: #ccc;
}
.content {
    padding: 40px;
}
.header {
    height: 10%;
    width: 100%;
    font-size: 24px;
    font-weight: 700;
    line-height: 60px;
    text-align: center;
}
.body {
    height: 100%;
    width: 100%;
    text-align: center;
}
.chartBox {
    width: 100%;
    height: 60%;
    margin-bottom:40px;
}
</style>
<body>
    <div class="content">
        <div class="header"></div>
        <div class="body">
            <div class="chartBox" id="hotWork"></div>
            <div class="chartBox" id="bigDataWork"></div>
            <div class="chartBox" id="pieAvgMoneyCity"></div>
        </div>
    </div>
</body>
<script>
    //折线图   招聘数量最多的前十热门职位
    var hotWork = echarts.init(document.getElementById('hotWork'));     //获取div的id   实例化echarts组件
    var data_name = [{% for r in rs_ghw %} "{{r.job_name}}", {% endfor %}]     //将职位名job_name存放在一个数组中作为x轴数据
    var data_y = [{% for r in rs_ghw %}"{{r.job_number}}",{% endfor %}]     //将职位数量job_number存放在一个数组中作为y轴数据

    console.log('十大热门职位:' + data_name);
    console.log('数据分别为:' + data_y);
    console.log('最大值: ' + Math.max.apply(null, data_y) + ',    最小值:' + Math.min.apply(null, data_y));

    workOption = {
        title: {
            text: '职位分析',   //主标题
            subtext: '            ---10大热门职位分析',  //副标题
            x: '45%'    //设置标题位置
        },
        xAxis: {
            type: 'category',
            name: '岗位名称',
            data: data_name,
            axisLabel : {
                interval: 0,
                rotate: "25"    //x轴字体的旋转度
            }
        },
        yAxis: {
            name: '招聘数量',
            type: 'value'
        },
        series: [{
            data: data_y,
            type: 'line',   //设置图形为折线图
            label: {
                normal: {
                    show: true,
                    position: 'top'  //折线图顶部显示对应的x轴数值
                }
            }
        }]
    };
    hotWork.setOption(workOption);   //设置echarts的option参数  加载并显示图形

    //南丁格尔玫瑰图
    var pieAvgMoneyCity = echarts.init(document.getElementById('pieAvgMoneyCity'));

    var city = [{% for r in rs_gamc %}"{{r.city}}",{% endfor %}]
    var avg_money_pie_city =  [{% for r in rs_gamc %}{value:"{{r.avg_money}}", name:"{{r.city}}"},{% endfor %}]
    var avg_money_pie_bigdata =  [{% for r in rs_gamb %}{value:"{{r.avg_money}}", name:"{{r.city}}"},{% endfor %}]
    var avg_money_city = [{% for r in rs_gamc %} "{{r.avg_money}}", {% endfor %}]
    var avg_money_big_data = [{% for r in rs_gamb %}"{{r.avg_money}}",{% endfor %}]
    var dataInt_city = [];
    var dataInt_big_data = [];

    dataInt_city.forEach(function(data){
        avg_money_city.push(+parseInt(data));   ///遍历数组   将每个元素变成整型
    });
    dataInt_big_data.forEach(function(data){
        avg_money_big_data.push(+parseInt(data));   ///遍历数组   将每个元素变成整型
    });

    console.log('所有城市平均薪资:' + city);
    console.log('数据分别为:' + avg_money_city);
    console.log("所有城市最大平均薪资为" + Math.max.apply(null, avg_money_city));
    console.log("“大数据”相关职位城市招聘数据的平均工资" + city);
    console.log('数据分别为:' + avg_money_big_data);
    console.log("“大数据”相关职位最大平均薪资为" + Math.max.apply(null, avg_money_big_data));

    pieAvgMoneyCityOption = {
        title : {
            text: '所有城市招聘数据的平均工资 vs “大数据”相关职位所有城市招聘数据的平均工资',
            subtext: '南丁格尔玫瑰图',
            x:'center'
        },
        tooltip : {
            trigger: 'item',
            formatter: "{b}:{c}({d}%)"      //当鼠标移动到图形 显示数据(格式): 佛山(1111) 10%
        },
        legend: {
            x : 'center',
            y : 'bottom',
            data:city
        },
                color:[
            '#C1232B','#B5C334','#FCCE10','#E87C25','#27727B',
                             '#668ffe','#00ca54','#00dbfa','#f3006a','#60C0DD',
                             '#d714b7','#84433c','#f490f3','#000000','#26C0C0'],
        series : [
            {
                type:'pie',
                radius : [20, 110],     //图像的大小
                center : ['25%', '50%'],    //图形的位置
                roseType : 'radius',        //南丁格尔玫瑰图的参数
                data:avg_money_pie_city
            },
            {
                type:'pie',
                radius : [30, 110],
                center : ['75%', '50%'],
                roseType : 'area',
                data:avg_money_pie_bigdata
            }
        ]
    };
    pieAvgMoneyCity.setOption(pieAvgMoneyCityOption);


    //柱状图   "大数据"相关职位招聘数量
    var bigDataWork = echarts.init(document.getElementById('bigDataWork'));

    var job_name = [{% for r in rs_gbdw %} "{{r.job_name}}", {% endfor %}]
    var quantity = [{% for r in rs_gbdw %}"{{r.job_count}}",{% endfor %}]  // 获取元组中的第二个元素('AI大数据工程师', 1095)

    console.log('招聘职位:  ' + job_name);
    console.log('对应职位招聘数量:  ' + quantity);
    console.log('岗位需求量最大:  ' + Math.max.apply(null, quantity) + ',  岗位需求量最少:' +  Math.min.apply(null, quantity));
    bigdataworkOption = {
        title: {
            text: '大数据相关职位招聘数量',
            subtext: '               ----职位招聘对比',
            x: '45%',
            //modified 0523
            textStyle:{
                //文字颜色
                color:'#f30008',
                //字体风格,'normal','italic','oblique'
                fontStyle:'oblique',
                //字体粗细 'normal','bold','bolder','lighter',100 | 200 | 300 | 400...
                fontWeight:'bold',
                //字体系列
                fontFamily:'FangSong'
                //fontFamily: 'KaiTi'
                //字体大小
                //fontSize:18
            }
            //modified end
        },
        xAxis: {
            type: 'category',
            name: '职位名称',
            data: job_name,
            axisLabel : {
                interval: 0,
                rotate: "15"    //x轴字体的旋转度
            }
        },
        yAxis: {
            type: 'value',
            name: '招聘数量'
        },
        series: [{
            data: quantity,
            type: 'bar',
            label: {
                normal: {
                    show: true,
                    position: 'top'
                }
            },
            //modified0523
            itemStyle: {
               normal: {color: 'black'}
            }
            //modified end
        }]
    };
    bigDataWork.setOption(bigdataworkOption);

</script>

</html>

4.13运行Flask程序查看结果

(1)分析并统计招聘数量最多的前十名热门职位,分别使用折线图表达

(2)分析并统计招聘数量"大数据"相关职位招聘数量,分别使用柱状图表达。

(3)统计出全国某些城市指定招聘岗位平均工资,通过南丁格尔玫瑰图进行呈现。

results matching ""

    No results matching ""