```python
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.font_manager as mfm
import os
import numpy as np
import scipy as sp
from datetime import timedelta
import datetime
import yfinance as yf
```

```python
_CHN_FONT_ = None
_FONT_PROP_ = None
_CHN_EN_DICT_ = './data/locationDict.csv'
_CHN_LatLng_DICT_ = './data/lat_lng.csv'
# The province names in China
# Ignore Hong Kong, Macau, Taiwan and Tibet
# English name
names_province = ['Anhui', 'Beijing', 'Chongqing', 'Fujian', 'Gansu', 'Guangdong', 'Guangxi', 'Guizhou',
                  'Hainan', 'Hebei', 'Heilongjiang', 'Henan', 'Hubei', 'Hunan', 'Inner Mongolia', 'Jiangsu',
                  'Jiangxi', 'Jilin', 'Liaoning', 'Ningxia', 'Qinghai', 'Shaanxi', 'Shandong', 'Shanghai',
                  'Shanxi', 'Sichuan', 'Tianjin', 'Xinjiang', 'Yunnan', 'Zhejiang']
# Chinese name
names_province_cn = ['安徽省', '北京市', '重庆市', '福建省', '甘肃省', '广东省', '广西壮族自治区', '贵州省',
                     '海南省', '河北省', '黑龙江省', '河南省', '湖北省', '湖南省', '内蒙古自治区', '江苏省',
                     '江西省', '吉林省', '辽宁省', '宁夏回族自治区', '青海省', '陕西省', '山东省', '上海市',
                     '山西省', '四川省', '天津市', '新疆维吾尔自治区', '云南省', '浙江省']

# A one to one corrspondence with the names used by Pyecharts
pyecharts_province_dict = {'北京市':'北京', '天津市':'天津', '河北省':'河北', '山西省':'山西',
                           '内蒙古自治区':'内蒙古', '辽宁省':'辽宁', '吉林省':'吉林', '黑龙江省':'黑龙江',
                           '上海市':'上海', '江苏省':'江苏', '浙江省':'浙江', '安徽省':'安徽',
                           '福建省':'福建', '江西省':'江西', '山东省':'山东', '河南省':'河南',
                           '湖北省':'湖北','湖南省':'湖南', '广东省':'广东', '广西壮族自治区':'广西',
                           '海南省':'海南', '重庆市':'重庆', '四川省':'四川', '贵州省':'贵州',
                           '云南省':'云南', '西藏自治区':'西藏', '陕西省':'陕西', '甘肃省':'甘肃',
                           '青海省':'青海', '宁夏回族自治区':'宁夏', '新疆维吾尔自治区':'新疆',
                           '香港特别行政区':'香港', '澳门特别行政区':'澳门','台湾省':'台湾'}

# English names of the capital cities of provinces
provincial_capital_dict = {'Anhui':'Hefei', 'Fujian':'Fuzhou', 'Gansu':'Lanzhou','Guangdong':'Guangzhou','Guizhou':'Guiyang',
                           'Hainan':'Haikou', 'Hebei':'Shijiazhuang','Heilongjiang':'Harbin', 'Henan':'Zhengzhou',
                           'Hubei':'Wuhan', 'Hunan':'Changsha', 'Jiangsu':'Nanjing', 'Jiangxi':'Nanchang',
                           'Jilin':'Changchun', 'Liaoning':'Shenyang', 'Qinghai':'Xining', 'Shaanxi':'Xi’an',
                           'Shandong':'Jinan', 'Shanxi':'Taiyuan', 'Sichuan':'Chengdu', 'Yunnan':'Kunming',
                           'Zhejiang':'Hangzhou', 'Guangxi':'Nanning','Inner Mongolia':'Hohhot', 'Ningxia':'Yinchuan',
                           'Xinjiang':'Ürümqi', 'Tibet':'Lhasa', 'Beijing':'Beijing', 'Chongqing':'Chongqing',
                           'Shanghai':'Shanghai', 'Tianjin':'Tianjin'}

provincial_capital_dict_cn = {'Anhui':'合肥市', 'Fujian':'福州市', 'Gansu':'兰州市','Guangdong':'广州市','Guizhou':'贵阳市',
                           'Hainan':'海口市', 'Hebei':'石家庄市','Heilongjiang':'哈尔滨市', 'Henan':'郑州市',
                           'Hubei':'武汉市', 'Hunan':'长沙市', 'Jiangsu':'南京市', 'Jiangxi':'南昌市',
                           'Jilin':'长春市', 'Liaoning':'沈阳市', 'Qinghai':'西宁市', 'Shaanxi':'西安市',
                           'Shandong':'济南市', 'Shanxi':'太原市', 'Sichuan':'成都市', 'Yunnan':'昆明市',
                           'Zhejiang':'杭州市', 'Guangxi':'南宁市','Inner Mongolia':'呼和浩特市', 'Ningxia':'银川市',
                           'Xinjiang':'乌鲁木齐市', 'Tibet':'拉萨市', 'Beijing':'北京市', 'Chongqing':'重庆市',
                           'Shanghai':'上海市', 'Tianjin':'天津市'}

# Populations of provinces
# data source: http://www.chamiji.com/2019chinaprovincepopulation
# unit 10,000
provincial_population_dict = {'Anhui':6323.6, 'Fujian':3941, 'Gansu': 2637.26,'Guangdong':11346,'Guizhou':3600,
                           'Hainan':934.32, 'Hebei':7556.30,'Heilongjiang':3773.1, 'Henan':9605,
                           'Hubei':5917, 'Hunan':6898.8, 'Jiangsu':8050.7, 'Jiangxi':4647.6,
                           'Jilin':2704.06, 'Liaoning':4359.3, 'Qinghai':603.23, 'Shaanxi':3864.4,
                           'Shandong':10047.24, 'Shanxi':3718.34, 'Sichuan':8341, 'Yunnan':4800.5,
                           'Zhejiang':5737, 'Guangxi':4926,'Inner Mongolia':2534.0, 'Ningxia':688.11,
                           'Xinjiang':2486.76, 'Tibet':337.15, 'Beijing':2154.2, 'Chongqing': 3101.79,
                           'Shanghai':2423.78, 'Tianjin':1559.60}
# Populations of cities
# data source: http://www.chamiji.com/
# unit 10,000
city_population_dict = {'北京市':2154.2, '天津市':1559.60, '上海市':2423.78, '重庆市':3101.79,
                        '亳州市':523.7, '六安市':483.7, '合肥市':808.7, '安庆市':469.1, '宣城市':264.8, '宿州市':568.14, # 安徽省
                        '池州市':147.4, '淮北市':225.4, '淮南市':349.0, '滁州市':411.4, '芜湖市':374.8, '蚌埠市':339.20,
                        '铜陵市':162.9, '阜阳市':820.7, '马鞍山市':233.7, '黄山市':140.7,
                        '南京市':843.62, '南通市':731, '宿迁市':492.59, '常州市':472.9, '徐州市':880.20, '扬州市':453.1, # 江苏省
                        '无锡市':657.45, '泰州市':463.57, '淮安市':492.50, '盐城市':720, '苏州市':1072.17, '连云港市':452.0,
                        '镇江市':319.64,
                        '东营市':217.21, '临沂市':1062.4, '威海市':283, '德州市':581, '日照市':293.03, '枣庄市':392.73, # 山东省
                        '泰安市':564.0, '济南市':746.04, '济宁市':834.59, '淄博市':470.2, '滨州市':392.25, '潍坊市':937.3,
                        '烟台市':712.18, '聊城市':606.43, '菏泽市':876.5, '青岛市':939.48,
                        '丽水市':219.25, '台州市':613.90, '嘉兴市':472.6, '宁波市':820.2, '杭州市':980.6, '温州市':925, # 浙江省
                        '湖州市':301.09, '绍兴市':503.5, '舟山市':117.3, '衢州市':220.9, '金华市':560.4,
                        '三明市':258, '南平市':269, '厦门市':411, '宁德市':291, '泉州市':870, '漳州市':514.0, # 福建省
                        '福州市':774, '莆田市':290.0, '龙岩市':264,
                        '东莞市':834.25, '中山市':326, '云浮市':326, '佛山市':765.67, '广州市':1449.84, '惠州市':477.70, # 广东省
                        '揭阳市':608.6, '梅州市':437.43, '汕头市':560.82, '汕尾市':305.33, '江门市':456.17, '河源市':309.11,
                        '深圳市':1252.83, '清远市':386.0, '湛江市':730.5, '潮州市':265.08, '珠海市':176.54, '肇庆市':411.54,
                        '茂名市':620.41, '阳江市': 254.29, '韶关市':297.92,
                        '北海市':166.33, '南宁市':715.33, '崇左市':208.68, '来宾市':221.86, '柳州市':400.00, '桂林市':505.75, # 广西壮族自治区
                        '梧州市':303.7, '河池市':352.35, '玉林市': 581.08, '百色市':364.65, '贵港市':437.54, '贺州市':205.67,
                        '钦州市':328, '防城港市':94.02,
                        '万宁市':float("NaN"), '三亚市':76.42, '儋州市':90.57, '海口市':227.21, '三沙市':float("NaN"), '东方市':float("NaN"), # 海南省 ##########
                        '临高县':float("NaN"), '乐东黎族自治县':float("NaN"),'五指山市':float("NaN"),'保亭黎族苗族自治县':float("NaN"), '定安县':float("NaN"),'屯昌县':float("NaN"),
                        '文昌市':float("NaN"),'昌江黎族自治县':float("NaN"),'澄迈县':float("NaN"),'琼中黎族苗族自治县':float("NaN"),'琼海市':float("NaN"),'白沙黎族自治县':float("NaN"),
                        '陵水黎族自治县':float("NaN"),
                        '保定市':1046.92, '唐山市': 789.7, '廊坊市':467.8, '张家口市':443.3, '承德市':356.50, '沧州市':755.49, # 河北省
                        '石家庄市':1087.99, '秦皇岛市':311.08, '衡水市':446.04, '邢台市':735.16, '邯郸市':951.11,
                        '乌兰察布市':210.25, '乌海市':56.11, '兴安盟':160.42, '包头市':287.8, '呼伦贝尔市':252.92, '呼和浩特市':311.5, # 内蒙古自治区
                        '巴彦淖尔市':168.5, '赤峰市':431.5, '通辽市':312.87, '鄂尔多斯市':206.87, '锡林郭勒盟':105.16, '阿拉善盟':24.8,
                        '临汾市':450.03, '吕梁市':388.56, '大同市':345.60, '太原市':442.15, '忻州市':317.20, '晋中市':338.15, # 山西省
                        '晋城市':234.31, '朔州市':178.12, '运城市':535.97, '长治市':346.8, '阳泉市':141.44,
                        '三门峡市':227.29, '信阳市':647.41, '南阳市':1001.36, '周口市':867.78, '商丘市':732.53, '安阳市':517.6, # 河南省
                        '平顶山市':520.77, '开封市':456.49, '新乡市':579.41, '洛阳市':688.85, '济源市':73.27, '漯河市':266.53,
                        '濮阳市':360.94, '焦作市':359.07, '许昌市':443.74, '郑州市':1013.6, '驻马店市':619.02, '鹤壁市':162.73,
                        '娄底市':393.18, '岳阳市':579.71, '常德市':582.7, '张家界市':153.79, '怀化市':497.96, '株洲市':402.08, # 湖南省
                        '永州市':545.21, '湘潭市':286.5, '湘西土家族苗族自治州':264.95, '益阳市':441.38, '衡阳市':724.34, '邵阳市':737.05,
                        '郴州市':474.5, '长沙市':815.47,
                        '仙桃市':154.45, '十堰市':341.8, '咸宁市':253.51, '天门市':128.35, '孝感市':491.50, '宜昌市':413.56, # 湖北省 ##########
                        '恩施土家族苗族自治州':336.10, '武汉市':1089.29, '潜江市': float("NaN"), '神农架林区':7.68, '荆州市':564.17, '荆门市':290.15,
                        '襄阳市':565.4, '鄂州市':107.69, '随州市':221.05, '黄冈市':634.1, '黄石市':247.05,
                        '上饶市':678.34, '九江市':487.33, '南昌市':546.35, '吉安市':494.19, '宜春市':555.37, '抚州市':403.10, # 江西省 2018
                        '新余市':118.07, '景德镇市':166.49, '萍乡市':192.50, '赣州市':861.2, '鹰潭市':116.75,
                        '六盘水市':292.41, '安顺市':234.44, '毕节市':665.97, '贵阳市':480.20, '遵义市':624.83, '铜仁市':315.69, # 贵州省 2018
                        '黔东南苗族侗族自治州':352.37, '黔南布依族苗族自治州':327.1, '黔西南布依族苗族自治州':286,
                        '乐山市':327.21, '内江市':375.37, '凉山彝族自治州':521.29, '南充市':641.79, '宜宾市':453, '巴中市':331.67, # 四川省 2018
                        '广元市':266.00, '广安市':325.0, '德阳市':353.2, '成都市':1604.5, '攀枝花市':123.61, '泸州市':431.72,
                        '甘孜藏族自治州':118.63, '眉山市':297.48, '绵阳市':483.56, '自贡市':290.14, '资阳市':255.3, '达州市':568.95,
                        '遂宁市':323.59, '阿坝藏族羌族自治州':94.01, '雅安市':153.78,
                        '临沧市':252.60, '丽江市':129.0, '保山市':261.4, '大理白族自治州':361.88, '德宏傣族景颇族自治州':130.90, '怒江傈僳族自治州':54.7, # 云南省 2018
                        '文山壮族苗族自治州':363.6, '昆明市':678.3, '昭通市': 553.7, '普洱市':262.7, '曲靖市':612.2, '楚雄彝族自治州':274.40,
                        '玉溪市':238.1, '红河哈尼族彝族自治州':471.3, '西双版纳傣族自治州':118.0, '迪庆藏族自治州':41.2,
                        '咸阳市':437.6, '商洛市':238.13, '安康市':266.1, '宝鸡市':378.10, '延安市':226.31, '榆林市':340.33, # 陕西省
                        '汉中市':344.93, '渭南市':538.29, '西安市':953.44, '铜川市':83.34,
                        '临夏回族自治州':204.41, '兰州市':372.96, '嘉峪关市':24.98, '天水市':333.98, '定西市':280.84, '平凉市':211.28, # 甘肃省
                        '庆阳市':200.55, '张掖市':122.93, '武威市':182.53, '甘南藏族自治州':74.23, '白银市':172.93, '酒泉市':112.36,
                        '金昌市':46.92, '陇南市':287.42,
                        '果洛藏族自治州':20.57, '海东市':147.08, '海北藏族自治州':28.3, '海南藏族自治州':47.24, '海西蒙古族藏族自治州':51.52, '玉树藏族自治州':40.95, # 青海省
                        '西宁市':235.50, '黄南藏族自治州':27.42,
                        '乌鲁木齐市':222.61, '五家渠市':float("NaN"), '伊犁哈萨克自治州':461.71, '克孜勒苏柯尔克孜自治州':62.06, '克拉玛依市':44.28, '北屯市':float("NaN"), # 新疆维吾尔自治区 2018 ##########
                        '博尔塔拉蒙古自治州':47.54, '双河市':float("NaN"), '可克达拉市':float("NaN"), '吐鲁番市':63.73, '和田地区':252.28, '哈密市':56.11,
                        '喀什地区':464.97, '图木舒克市':float("NaN"), '塔城地区':15.2, '巴音郭楞蒙古自治州':127.93, '昆玉市':float("NaN"), '昌吉回族自治州':161,
                        '石河子市':float("NaN"), '铁门关市':float("NaN"), '阿克苏地区':254.6, '阿勒泰地区':67.16, '阿拉尔市':float("NaN"),
                        '中卫市':115.75, '吴忠市':140.37, '固原市':122.82, '石嘴山市':80.29, '银川市':222.54, # 宁夏回族自治区 2018
                        '吉林市':415.35, '四平市':320.4, '延边朝鲜族自治州':210.14, '松原市':275.41, '白城市':190.9, '白山市':119.5, # 吉林省 2018
                        '辽源市':117.94, '通化市':217.15, '长春市':748.9,
                        '丹东市':239.5, '大连市':698.75, '抚顺市':210.7, '朝阳市':295, '本溪市':147.63, '沈阳市':829.4, # 辽宁省 2018
                        '盘锦市':143.65, '营口市':243.8, '葫芦岛市':277.0, '辽阳市':183.7, '铁岭市':299.8, '锦州市':296.4,
                        '阜新市':186.2, '鞍山市':344.0,
                        '七台河市':78.6, '伊春市':115.9, '佳木斯市':234.5, '双鸭山市':142.3, '哈尔滨市':955.0, '大兴安岭地区':43.93, # 黑龙江省 2018
                        '大庆市':273.1, '牡丹江市':254.8, '绥化市':527.6, '鸡西市':175, '鹤岗市':100.9, '黑河市':160.5,
                        '齐齐哈尔市':533.7
                       }

# Reset the unit of populations to be 1 instead of 10,000
provincial_population_dict.update((x, y*1e4) for x, y in provincial_population_dict.items())
city_population_dict.update((x, y*1e4) for x, y in city_population_dict.items())

# 应急响应
# 根据突发公共卫生事件性质、危害程度、涉及范围，突发公共卫生事件划分为特别重大（Ⅰ级）、重大（Ⅱ级）、较大（Ⅲ级）和一般（Ⅳ级）四级。
################################################
# 重大突发公共卫生事件一级响应
# source: 百度百科 2020年新型冠状病毒疫情
# high level responses
# https://baijiahao.baidu.com/s?id=1656586269875684850&wfr=spider&for=pc 安徽，广东，湖北，湖南
# http://www.bjnews.com.cn/news/2020/01/24/679091.html 福建
# https://www.thepaper.cn/newsDetail_forward_5632391 甘肃
# http://www.bjnews.com.cn/news/2020/01/24/679044.html 贵州
# http://news.sina.com.cn/c/2020-01-25/doc-iihnzahk6236313.shtml 海南
# http://www.bjnews.com.cn/news/2020/01/24/679103.html 河北
# https://www.thepaper.cn/newsDetail_forward_5631343 黑龙江
# https://news.sina.com.cn/c/2020-01-25/doc-iihnzahk6246104.shtml 河南
# http://www.xhby.net/js/sh/202001/t20200125_6484737.shtml 江苏 江苏省人民政府决定，自2020年1月24日24时起，...
# https://baijiahao.baidu.com/s?id=1656604836051211594&wfr=spider&for=pc 江西，云南
# https://baijiahao.baidu.com/s?id=1656695669637427999&wfr=spider&for=pc 吉林
# https://www.thepaper.cn/newsDetail_forward_5632430 辽宁
# https://m.chinanews.com/wap/detail/zw/gn/2020/01-25/9069563.shtml 青海 青海省人民政府决定，从2020年1月25日24时起，启动青海省重大突发公共卫生事件一级响应。
# http://m.news.cctv.com/2020/01/25/ARTIFJkv1OzVvBRdIenCLFzZ200125.shtml 陕西
# http://www.mrjjxw.com/articles/2020-01-24/1402733.html 山东
# https://www.thepaper.cn/newsDetail_forward_5633430 山西 18:00
# http://www.sc.gov.cn/10462/10464/10797/2020/1/24/3ff65fa2d2054f78b7c551c7ee961167.shtml 四川 1月24日下午
# https://news.163.com/20/0124/23/F3MMST4V0001899O.html 广西 自治区新型冠状病毒感染的肺炎疫情联防联控工作领导小组决定于2020年1月24日23时起启动重大突发公共卫生事件I级响应。
# http://www.gov.cn/xinwen/2020-02/26/content_5483388.htm 内蒙古
# https://wap.peopleapp.com/article/5075403/4970020 宁夏
# https://news.sina.com.cn/c/2020-01-25/doc-iihnzahk6244183.shtml 新疆
# http://www.bjnews.com.cn/feature/2020/01/29/681402.html 西藏
# https://baijiahao.baidu.com/s?id=1656590920870989133&wfr=spider&for=pc 北京
# http://chongqing.163.com/20/0124/19/F3M9IG1B04218FF3.html 重庆
# http://news.sina.com.cn/c/2020-01-24/doc-iihnzhha4524474.shtml 上海
# http://www.xinhuanet.com/politics/2020-01/24/c_1125500208.htm 天津
provincial_response_1_dict = {'Anhui': datetime.date(int(2020),int(1),int(24)), 'Fujian': datetime.date(int(2020),int(1),int(24)),
                            'Gansu': datetime.date(int(2020),int(1),int(25)), 'Guangdong': datetime.date(int(2020),int(1),int(23)),
                            'Guangxi': datetime.date(int(2020),int(1),int(24)), 'Guizhou': datetime.date(int(2020),int(1),int(24)),
                            'Hainan': datetime.date(int(2020),int(1),int(25)), 'Hebei': datetime.date(int(2020),int(1),int(24)),
                            'Heilongjiang': datetime.date(int(2020),int(1),int(25)), 'Henan': datetime.date(int(2020),int(1),int(25)),
                            'Hubei': datetime.date(int(2020),int(1),int(24)), 'Hunan': datetime.date(int(2020),int(1),int(23)),
                            'Inner Mongolia': datetime.date(int(2020),int(1),int(25)), 'Jiangsu': datetime.date(int(2020),int(1),int(25)),
                            'Jiangxi': datetime.date(int(2020),int(1),int(24)), 'Jilin': datetime.date(int(2020),int(1),int(25)),
                            'Liaoning': datetime.date(int(2020),int(1),int(25)), 'Ningxia': datetime.date(int(2020),int(1),int(25)),
                            'Qinghai': datetime.date(int(2020),int(1),int(26)), 'Shaanxi': datetime.date(int(2020),int(1),int(25)),
                            'Shandong': datetime.date(int(2020),int(1),int(24)), 'Shanxi': datetime.date(int(2020),int(1),int(25)),
                            'Sichuan': datetime.date(int(2020),int(1),int(24)), 'Xinjiang': datetime.date(int(2020),int(1),int(25)),
                            'Yunnan': datetime.date(int(2020),int(1),int(24)), 'Zhejiang': datetime.date(int(2020),int(1),int(23)),
                            'Beijing': datetime.date(int(2020),int(1),int(24)), 'Chongqing': datetime.date(int(2020),int(1),int(24)),
                            'Shanghai': datetime.date(int(2020),int(1),int(24)), 'Tianjin': datetime.date(int(2020),int(1),int(24)),
                            'Tibet': datetime.date(int(2020),int(1),int(29))}
################################################
# 重大突发公共卫生事件二级响应
# http://www.gov.cn/xinwen/2020-02/26/content_5483389.htm 安徽自2020年2月25日12时起
# http://www.gov.cn/xinwen/2020-02/27/content_5483772.htm 福建 自2020年2月26日24时起，将我省新冠肺炎疫情中风险区由执行突发公共卫生事件一级响应调整为执行新冠肺炎疫情防控省级二级响应，将低风险区由执行突发公共卫生事件一级响应调整为执行新冠肺炎疫情防控省级三级响应。
# http://www.gansu.gov.cn/art/2020/3/1/art_35_448301.html 甘肃 2月21日，在突发公共卫生事件一级应急响应28天之后，甘肃省新冠肺炎疫情联防联控领导小组办公室发出公告，决定自当日14时起，将全省新冠肺炎疫情防控级别由一级应急响应调整为省级三级应急响应。
# http://news.cnr.cn/native/city/20200224/t20200224_524989445.shtml 广东 今天（2月24日）下午，将重大突发公共卫生事件一级响应调整为二级响应相关情况。
# http://m.guilinlife.com/news/article/docid/137-458196 广西 自2月24日20时起，一级响应调整为三级响应。
# http://www.xinhuanet.com/local/2020-02/24/c_1125617023.htm 贵州 自2020年2月23日24时起，一级响应调整为省级三级响应。
# http://www.gov.cn/xinwen/2020-02/26/content_5483666.htm 海南 新华社海口2月26日电（记者 陈凯姿）海南省新冠肺炎防控领导小组26日决定，自当天17时起，一级响应调整为三级响应。
# http://www.gov.cn/xinwen/2020-04/29/content_5507536.htm 河北 天津 河北省决定自2020年4月30日起，将全省突发公共卫生事件应急响应级别由一级调整为二级。自2020年4月30日零时起，天津市重大突发公共卫生事件应急响应级别由一级调整为二级。
# http://china.caixin.com/2020-03-25/101533875.html 黑龙江 自3月25日18时起，二级响应调整为三级响应。此前，3月4日，由一级调整为二级。
# https://www.yicai.com/news/100554642.html 河南 自3月19日0时起
# http://www.xinhuanet.com/politics/2020-06/12/c_1126108284.htm 湖北 5月2日全省突发公共卫生应急响应级别由一级调整为二级以来 6月13日零时起，湖北省突发公共卫生应急响应级别由二级调整为三级
# http://m.news.cctv.com/2020/03/10/ARTItx6cc0xA5qNi2EeITr1W200310.shtml 湖南 10日23时
# http://www.nmg.xinhuanet.com/xwzx/2020-02/25/c_1125625406.htm 内蒙古 自2020年2月25日24时起，由一级应急响应调整为自治区级三级应急响应。
# https://www.wjdaily.com/news/239671 江苏 2020年2月24日24时
# http://china.caixin.com/2020-03-12/101527309.html 江西 3月12日9时
# http://www.gov.cn/xinwen/2020-02/26/content_5483665.htm 吉林 2020年2月26日15时
# https://china.huanqiu.com/article/9CaKrnKpyfw 辽宁 自2020年2月22日9时起，由一级应急响应调整为省级三级应急响应。
# http://www.xinhuanet.com/2020-05/06/c_1125949706.htm 宁夏 并自2月28日18时起 自2020年5月6日17时起
# http://www.xinhuanet.com/2020-02/26/c_1125626759.htm 青海 自2020年2月26日12时起，由一级响应调整为三级响应。
# http://www.xinhuanet.com/local/2020-02/27/c_1125636256.htm 陕西 自2020年2月28日0时起，由一级应急响应调整为省级三级应急响应。
# http://china.caixin.com/2020-03-08/101525427.html 山东 自2020年3月7日24时起，由重大突发公共卫生事件Ⅰ级响应调整为Ⅱ级响应。
# http://www.sx.xinhuanet.com/2020-02/24/c_1125616195.htm 山西 自2020年2月24日零时起
# http://www.xinhuanet.com/local/2020-02/25/c_1125625913.htm 四川 2020年2月26日0时起
# http://www.xinhuanet.com/politics/2020-02/26/c_1125627838.htm 新疆 自2020年2月25日24时起
# http://www.xinhuanet.com/politics/2020-02/24/c_1125616933.htm 云南 自2020年2月24日零时起，由一级应急响应调整为省级三级应急响应。
# http://www.xinhuanet.com/politics/2020-03/02/c_1125651043.htm 浙江 3月2日
# http://www.xinhuanet.com/local/2020-04/29/c_1125923588.htm 北京 自4月30日零时起
# https://www.cq.gov.cn/zwxx/jrcq/202003/t20200310_5675212.html 重庆 自3月10日24时起
# http://www.xinhuanet.com/politics/2020-03/23/c_1125754077.htm 上海 自3月24日零时起
# http://www.xinhuanet.com/local/2020-04/29/c_1125924212.htm 天津 自4月30日零时起
# https://china.huanqiu.com/article/9CaKrnKpLmO 西藏 自2020年3月6日24时起将西藏自治区突发公共卫生事件应急响应级别调整为Ⅱ级，部分地区按Ⅲ级响应管理。
provincial_response_2_dict = {'Anhui': datetime.date(int(2020),int(2),int(25)), 'Fujian': datetime.date(int(2020),int(2),int(27)),
                            'Gansu': None, 'Guangdong': datetime.date(int(2020),int(2),int(24)),
                            'Guangxi': None, 'Guizhou': None,
                            'Hainan': None, 'Hebei': datetime.date(int(2020),int(4),int(30)),
                            'Heilongjiang': datetime.date(int(2020),int(3),int(4)), 'Henan': datetime.date(int(2020),int(3),int(19)),
                            'Hubei': datetime.date(int(2020),int(5),int(2)), 'Hunan': datetime.date(int(2020),int(3),int(10)),
                            'Inner Mongolia': None, 'Jiangsu': datetime.date(int(2020),int(2),int(25)),
                            'Jiangxi': datetime.date(int(2020),int(3),int(12)), 'Jilin': datetime.date(int(2020),int(2),int(26)),
                            'Liaoning': None, 'Ningxia': datetime.date(int(2020),int(2),int(28)),
                            'Qinghai': None, 'Shaanxi': None,
                            'Shandong': datetime.date(int(2020),int(3),int(8)), 'Shanxi': datetime.date(int(2020),int(2),int(24)),
                            'Sichuan': datetime.date(int(2020),int(2),int(26)), 'Xinjiang': datetime.date(int(2020),int(2),int(26)),
                            'Yunnan': None, 'Zhejiang': datetime.date(int(2020),int(3),int(2)),
                            'Beijing': datetime.date(int(2020),int(4),int(30)), 'Chongqing': datetime.date(int(2020),int(3),int(11)),
                            'Shanghai': datetime.date(int(2020),int(3),int(24)), 'Tianjin': datetime.date(int(2020),int(4),int(30)),
                            'Tibet': datetime.date(int(2020),int(3),int(7))}
################################################
# http://www.xinhuanet.com/local/2020-03/15/c_1125716594.htm 安徽 自2020年3月15日18时起
# http://www.fuzhou.gov.cn/zgfzzt/ylws/tfggwssjyj/202005/t20200503_3269090.htm 福建 自2020年3月19日12时起
# http://www.xinhuanet.com/politics/2020-05/08/c_1125956046.htm 广东 自2020年5月9日零时起
# http://www.xinhuanet.com/politics/2020-06/05/c_1126079750.htm 河北 自2020年6月6日起
# http://www.xinhuanet.com/politics/2020-03/25/c_1125766966.htm 黑龙江 自3月25日18时起
# http://www.xinhuanet.com/local/2020-05/05/c_1125944833.htm 河南 自5月6日零时起
# http://www.xinhuanet.com/politics/2020-06/12/c_1126108036.htm 湖北 自6月13日零时起
# http://www.xinhuanet.com/local/2020-03/31/c_1125795941.htm 湖南 3月31日电
# http://www.js.xinhuanet.com/2020-03/28/c_1125780043.htm 江苏 自2020年3月27日24时起
# http://www.jx.xinhuanet.com/2020-03/20/c_1125744168.htm 江西 自3月20日18时起
# http://www.jl.xinhuanet.com/2020-03/20/c_1125742524.htm 吉林 自2020年3月20日14时起
# http://www.xinhuanet.com/2020-05/06/c_1125949706.htm 宁夏 自2020年5月6日17时起
# https://www.sohu.com/a/393095409_260616 山东 自2020年5月6日0时起
# http://m.xinhuanet.com/sx/2020-03/10/c_1125688119.htm 山西 自2020年3月10日零时
# http://www.sc.xinhuanet.com/content/2020-03/24/c_1125762537.htm 四川 自3月25日零时起
# http://www.xinhuanet.com/local/2020-03/08/c_1125681321.htm 新疆 自2020年3月7日24时起
# http://m.xinhuanet.com/2020-03/23/c_1125755679.htm 浙江 3月23日电
# http://m.xinhuanet.com/2020-06/05/c_1126078726.htm 北京 6月6日零时起
# http://www.xinhuanet.com/politics/2020-03/24/c_1125761972.htm 重庆 从3月24日24时起
# http://m.xinhuanet.com/2020-05/08/c_1125957038.htm 上海 自5月9日零时起
# http://m.xinhuanet.com/2020-06/05/c_1126079845.htm 天津 自2020年6月6日零时起
# http://www.zytzb.gov.cn/mzdqzxd/334707.jhtml 西藏 西藏突发公共卫生事件应急响应级别已于3月28日下调至三级
provincial_response_3_dict = {'Anhui': datetime.date(int(2020),int(3),int(15)), 'Fujian': datetime.date(int(2020),int(3),int(19)),
                            'Gansu': datetime.date(int(2020),int(2),int(21)), 'Guangdong': datetime.date(int(2020),int(5),int(9)),
                            'Guangxi': datetime.date(int(2020),int(2),int(24)), 'Guizhou': datetime.date(int(2020),int(2),int(24)),
                            'Hainan': datetime.date(int(2020),int(2),int(26)), 'Hebei': datetime.date(int(2020),int(6),int(6)),
                            'Heilongjiang': datetime.date(int(2020),int(3),int(25)), 'Henan': datetime.date(int(2020),int(5),int(6)),
                            'Hubei': datetime.date(int(2020),int(6),int(13)), 'Hunan': datetime.date(int(2020),int(3),int(31)),
                            'Inner Mongolia': datetime.date(int(2020),int(2),int(26)), 'Jiangsu': datetime.date(int(2020),int(3),int(28)),
                            'Jiangxi': datetime.date(int(2020),int(3),int(20)), 'Jilin': datetime.date(int(2020),int(3),int(20)),
                            'Liaoning': datetime.date(int(2020),int(2),int(22)), 'Ningxia': datetime.date(int(2020),int(5),int(6)),
                            'Qinghai': datetime.date(int(2020),int(2),int(26)), 'Shaanxi': datetime.date(int(2020),int(2),int(28)),
                            'Shandong': datetime.date(int(2020),int(5),int(6)), 'Shanxi': datetime.date(int(2020),int(3),int(10)),
                            'Sichuan': datetime.date(int(2020),int(3),int(25)), 'Xinjiang': datetime.date(int(2020),int(3),int(8)),
                            'Yunnan': datetime.date(int(2020),int(2),int(24)), 'Zhejiang': datetime.date(int(2020),int(3),int(23)),
                            'Beijing': datetime.date(int(2020),int(6),int(6)), 'Chongqing': datetime.date(int(2020),int(3),int(25)),
                            'Shanghai': datetime.date(int(2020),int(5),int(9)), 'Tianjin': datetime.date(int(2020),int(6),int(6)),
                            'Tibet': datetime.date(int(2020),int(3),int(28))}

# Back and Forth
# http://www.xinhuanet.com/politics/2020-06/16/c_1126122434.htm 6月16日晚，由三级调至二级
# http://www.xinhuanet.com/local/2020-07/19/c_1126257663.htm 7月20日零时起，由二级调整为三级。
provincial_response_beijing_dict = {'1': datetime.date(int(2020),int(1),int(24)), '2': datetime.date(int(2020),int(4),int(30)),
                                   '3': datetime.date(int(2020),int(6),int(6)), '2x': datetime.date(int(2020),int(6),int(16)),
                                   '3x': datetime.date(int(2020),int(7),int(20))}

# Get the response data
def load_response_raw():
    provincial_response_dict = {}
    for key in (provincial_response_1_dict.keys()):
        provincial_response_dict.setdefault(key, []).append(provincial_response_1_dict[key])
        provincial_response_dict.setdefault(key, []).append(provincial_response_2_dict[key])
        provincial_response_dict.setdefault(key, []).append(provincial_response_3_dict[key])
    data_response_o = pd.DataFrame([(k, *v) for k, v in provincial_response_dict.items()])
    data_response_o.columns = ['province_name'] + [f'date_level{x}' for x in data_response_o.columns[1:]]
    ######
    data_response_o = data_response_o[data_response_o['province_name'] != 'Tibet']
    data_response_o = data_response_o.sort_values(by = 'province_name')
    data_response_o = data_response_o.reset_index(drop = True)
    ######
    data_response_o123 = data_response_o[data_response_o['date_level2'].values != None]
    data_response_o13 = data_response_o[data_response_o['date_level2'].values == None]
    # 特别重大（Ⅰ级）
    # https://en.wikipedia.org/wiki/COVID-19_pandemic_in_mainland_China
    data_response_1 = data_response_o123[['province_name', 'date_level1', 'date_level2']].copy()
    data_response_1.columns = ['province_name', 'start_date', 'end_date']
    data_response_1 = data_response_1.append(data_response_o13[['province_name', 'date_level1', 'date_level3']].rename(columns={'date_level1':'start_date', 'date_level3': 'end_date'}))
    data_response_1['level'] = 'Class 1'
    data_response_1 = data_response_1.sort_values(['province_name'])
    data_response_1 = data_response_1.reset_index(drop=True)
    # 重大（Ⅱ级）
    data_response_2 = data_response_o123[['province_name', 'date_level2', 'date_level3']].copy()
    data_response_2.columns = ['province_name', 'start_date', 'end_date']
    data_response_2['level'] = 'Class 2'
    data_response_2 = data_response_2.sort_values(['province_name'])
    data_response_2 = data_response_2.reset_index(drop=True)
    # 较大（Ⅲ级）
    data_response_3 = data_response_o[['province_name', 'date_level3']].copy()
    data_response_3.columns = ['province_name', 'start_date']
    data_response_3['end_date'] = max(datetime.date(int(2020),int(6),int(16)), max(data_response_3['start_date']) +  timedelta(days=1))
    data_response_3['level'] = 'Class 3'
    data_response_3 = data_response_3.sort_values(['province_name'])
    data_response_3 = data_response_3.reset_index(drop=True)
    data_response_3
    data_response = pd.concat([data_response_1, data_response_2, data_response_3])

    return data_response_o, data_response

# spring festival 2019-02-05, spring festival 2020-01-25
date_sp_2020 = datetime.date(int(2020),int(1),int(25)) # Saturday
date_sp_2019 = datetime.date(int(2019),int(2),int(5)) # Tuesday

# Obtain the latitude and longitude of a province from the local dictionary
# Save as a dictionary
def get_province_latlng_dict():
    province_latlng_dict = {}
    chn_latlng = pd.read_csv(_CHN_LatLng_DICT_, encoding='utf-8')
    for key in provincial_capital_dict:
        lat = chn_latlng[chn_latlng.city == provincial_capital_dict[key]]['lat'].tolist()[0]
        lng = chn_latlng[chn_latlng.city == provincial_capital_dict[key]]['lng'].tolist()[0]
        province_latlng_dict[key] = (lat, lng)
    return province_latlng_dict

# Save as a dataframe
def get_province_latlng_df():
    data = pd.DataFrame.from_dict(province_latlng_dict, orient='index', columns = ['lat', 'lng'])
    data = data.reset_index()
    data = data.rename(columns={'index':'province_name'})
    #data_gephi = data.rename(columns={'province_name':'id'})
    #data_gephi.to_csv(r'./data/data_network_P2P_gephi_id.csv', index = False)
    return data

province_latlng_dict = get_province_latlng_dict()
province_latlng_df = get_province_latlng_df()
```

```python
# Settings on the Chinese font
def set_font(font_file):
    if not os.path.exists(font_file):
        print(font_file + " not found.  If you wish to display Chinese characters in plots, please use set_font() to set the path to the font file.")
    else:
        global _CHN_FONT_, _FONT_PROP_
        _CHN_FONT_ = font_file
        _FONT_PROP_ = mfm.FontProperties(fname=_CHN_FONT_)
    return

set_font('./STFANGSO.TTF')   # for displaying Chinese characters in plots

def use_chn():
    return _CHN_FONT_ is None

# Add English name for a province or a city
def add_en_location(df, tag = 'city'):
    '''Add province_name_en, and city_name_en'''
    chn_en = pd.read_csv(_CHN_EN_DICT_, encoding='utf-8')
    translation = dict([t for t in zip(chn_en['Chinese'], chn_en['English'])])
    if tag == 'province':
        df['province_name_en'] = df['province_name'].replace(translation)
    elif tag == 'city':
        df['province_name_en'] = df['province_name'].replace(translation)
        df['city_name_en'] = df['city_name'].replace(translation)
    elif tag == 'network':
        df['source_en'] = df['source'].replace(translation)
        df['target_en'] = df['target'].replace(translation)
    return df
```

```python
# Data loading
_Data_PATH_ = './data/parameters/'
_Data_PATH_X_ = './data/parameters_x/'
_Data_PATH_XX_ = './data/parameters_xx/'
_Data_PATH_M_ = './data/parameters_m/'
_Data_PATH_MX_ = './data/parameters_mx/'
_Data_PATH_MXX_ = './data/parameters_mxx/'
_City_PATH_ = './data/data_DXY_city_all.csv'
_Province_PATH_ = './data/data_DXY_province_all.csv'
_Province_Domestic_PATH_ = './data/data_DXY_province_all_domestic.csv'
_Network_P2P_PATH_ = './data/data_network_P2P.csv'

def load_DXY_raw():
    raw_city = pd.read_csv(_City_PATH_)
    raw_province = pd.read_csv(_Province_PATH_)
    raw_province_domestic = pd.read_csv(_Province_Domestic_PATH_)
    raw_city['update_date'] = pd.to_datetime(raw_city['update_date'])
    raw_city['update_date'] = raw_city['update_date'].dt.date
    raw_province['update_date'] = pd.to_datetime(raw_province['update_date'])
    raw_province['update_date'] = raw_province['update_date'].dt.date
    raw_province_domestic['update_date'] = pd.to_datetime(raw_province_domestic['update_date'])
    raw_province_domestic['update_date'] = raw_province_domestic['update_date'].dt.date
    return raw_city, raw_province, raw_province_domestic

def load_network_raw():
    # Do not distinguish between move_in and move_out
    raw = pd.read_csv(_Network_P2P_PATH_)
    raw['update_date'] = pd.to_datetime(raw['update_date'])
    raw['update_date'] = raw['update_date'].dt.date
    return raw

def load_ind_simulation_raw():
    df_parameters_list = []
    df_estimation_list = []
    for name in names_province:
        df_parameters_single = pd.read_csv(_Data_PATH_ + name + '_parameters.csv')
        df_estimation_single = pd.read_csv(_Data_PATH_ + name + '_estimation.csv')
        df_parameters_list.append(df_parameters_single)
        df_estimation_list.append(df_estimation_single)
    return df_parameters_list, df_estimation_list

def load_ind_simulation_raw_x():
    df_parameters_list = []
    df_estimation_list = []
    for name in names_province:
        df_parameters_single = pd.read_csv(_Data_PATH_X_ + name + '_parameters.csv')
        df_estimation_single = pd.read_csv(_Data_PATH_X_ + name + '_estimation.csv')
        df_parameters_list.append(df_parameters_single)
        df_estimation_list.append(df_estimation_single)
    return df_parameters_list, df_estimation_list

def load_ind_simulation_raw_xx():
    df_parameters_list = []
    df_estimation_list = []
    for name in names_province:
        df_parameters_single = pd.read_csv(_Data_PATH_XX_ + name + '_parameters.csv')
        df_estimation_single = pd.read_csv(_Data_PATH_XX_ + name + '_estimation.csv')
        df_parameters_list.append(df_parameters_single)
        df_estimation_list.append(df_estimation_single)
    return df_parameters_list, df_estimation_list
```

```python
# Some utility functions
# Get the list of dates from the start_date to the end_date
def daterange(start_date, end_date):
    for n in range(int ((end_date - start_date).days) + 1):
        yield start_date + timedelta(n)

# Transform the migration edgelist to the migration matrix
def matrix_P2P(data_single, m):
    mindex = np.zeros((m, m))
    for i, name_i in enumerate(names_province):
        for j, name_j in enumerate(names_province):
            if i == j:
                mindex[i][j] = 0
            else:
                temp = data_single[(data_single.source_en == name_i) & (data_single.target_en == name_j)].value.tolist()
                if temp == []:
                    mindex[i][j] = 0
                else:
                    mindex[i][j] = temp[0]
    return mindex

# Perform the operation for all migration data from the start_date to the end_date
def matrix_P2P_all(data_network_P2P, start_date, end_date):
    mindex_list = []
    m = len(names_province)
    for item in daterange(start_date, end_date):
        data_single = data_network_P2P[data_network_P2P.update_date == item]
        mindex = matrix_P2P(data_single, m)
        mindex_list.append(mindex)
    return mindex_list
```

```python
# The state names in the US (including D.C.)
# Ignore oversea territories: American Samoa, Guam, Northern Mariana Islands, Puerto Rico, and Virgin Islands

names_state = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware',
                 'District of Columbia',
                 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas',
                 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi',
                 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York',
                 'North Carolina', 'North Dakota', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina',
                 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia',
                 'Wisconsin', 'Wyoming']

# The abbreviations of state names
names_state_short_dict = {
    'Alabama': 'AL', 'Alaska':'AK', 'Arizona':'AZ', 'Arkansas':'AR', 'California':'CA', 'Colorado':'CO', 'Connecticut':'CT', 'Delaware':'DE',
    'Florida':'FL', 'Georgia':'GA', 'Hawaii':'HI', 'Idaho':'ID', 'Illinois':'IL', 'Indiana':'IN', 'Iowa':'IA', 'Kansas':'KS',
    'Kentucky':'KY', 'Louisiana':'LA', 'Maine':'ME', 'Maryland':'MD', 'Massachusetts':'MA', 'Michigan':'MI', 'Minnesota':'MN', 'Mississippi':'MS',
    'Missouri':'MO', 'Montana':'MT', 'Nebraska':'NE', 'Nevada':'NV', 'New Hampshire':'NH', 'New Jersey':'NJ', 'New Mexico':'NM', 'New York':'NY',
    'North Carolina':'NC', 'North Dakota':'ND', 'Ohio':'OH', 'Oklahoma':'OK', 'Oregon':'OR', 'Pennsylvania':'PA', 'Rhode Island':'RI', 'South Carolina':'SC',
    'South Dakota':'SD', 'Tennessee':'TN', 'Texas':'TX', 'Utah':'UT', 'Vermont':'VT', 'Virginia':'VA', 'Washington':'WA', 'West Virginia':'WV',
    'Wisconsin':'WI', 'Wyoming':'WY',
    'District of Columbia': 'DC', 'Guam': 'Guam', 'Northern Mariana Islands':'Northern Mariana Islands',
    'Puerto Rico': 'PR', 'Virgin Islands': 'VI'}

# The populations of states
state_population_dict = {
    'Alabama': 4903185, 'Alaska':731545, 'Arizona':7278717, 'Arkansas':3017825,
    'California':39512223, 'Colorado':5758736, 'Connecticut':3565287, 'Delaware':973764,
    'Florida':21477737, 'Georgia':10617423, 'Hawaii':1415872, 'Idaho':1787065,
    'Illinois':12671821, 'Indiana':6732219, 'Iowa':3155070, 'Kansas':2913314,
    'Kentucky':4467673, 'Louisiana':4648794, 'Maine':1344212, 'Maryland':6045680,
    'Massachusetts':6949503, 'Michigan':9986857, 'Minnesota':5639632, 'Mississippi':2976149,
    'Missouri':6137428, 'Montana':1068778, 'Nebraska':1934408, 'Nevada':3080156,
    'New Hampshire':1359711, 'New Jersey':8882190, 'New Mexico':2096829, 'New York':19453561,
    'North Carolina':10488084, 'North Dakota':762062, 'Ohio':11689100, 'Oklahoma':3956971,
    'Oregon':4217737, 'Pennsylvania':12801989, 'Rhode Island':1059361, 'South Carolina':5148714,
    'South Dakota':884659, 'Tennessee':6833174, 'Texas':28995881, 'Utah':3205958,
    'Vermont':623989, 'Virginia':8535519, 'Washington':7614893, 'West Virginia':1792147,
    'Wisconsin':5822434, 'Wyoming':578759, 'District of Columbia': 705749, 'Guam': 165718,
    'Puerto Rico': 3193694, 'Virgin Islands': 104914}
```

```python
# The PVI values of states
df_state_pvi = pd.DataFrame(columns = ['state_name', 'partisan', 'value'])
df_state_pvi.loc[len(df_state_pvi)] = ['Alabama', 'R', 14]
df_state_pvi.loc[len(df_state_pvi)] = ['Alaska', 'R', 9]
df_state_pvi.loc[len(df_state_pvi)] = ['Arizona', 'R', 5]
df_state_pvi.loc[len(df_state_pvi)] = ['Arkansas', 'R', 15]

df_state_pvi.loc[len(df_state_pvi)] = ['California', 'D', 12]
df_state_pvi.loc[len(df_state_pvi)] = ['Colorado', 'D', 1]
df_state_pvi.loc[len(df_state_pvi)] = ['Connecticut', 'D', 6]
df_state_pvi.loc[len(df_state_pvi)] = ['Delaware', 'D', 6]

df_state_pvi.loc[len(df_state_pvi)] = ['Florida', 'R', 2]
df_state_pvi.loc[len(df_state_pvi)] = ['Georgia', 'R', 5]
df_state_pvi.loc[len(df_state_pvi)] = ['Hawaii', 'D', 18]

df_state_pvi.loc[len(df_state_pvi)] = ['Idaho', 'R', 19]
df_state_pvi.loc[len(df_state_pvi)] = ['Illinois', 'D', 7]

df_state_pvi.loc[len(df_state_pvi)] = ['Indiana', 'R', 9]
df_state_pvi.loc[len(df_state_pvi)] = ['Iowa', 'R', 3]
df_state_pvi.loc[len(df_state_pvi)] = ['Kansas', 'R', 13]
df_state_pvi.loc[len(df_state_pvi)] = ['Kentucky', 'R', 15]
df_state_pvi.loc[len(df_state_pvi)] = ['Louisiana', 'R', 11]
df_state_pvi.loc[len(df_state_pvi)] = ['Maine', 'D', 3]
df_state_pvi.loc[len(df_state_pvi)] = ['Maryland', 'D', 12]
df_state_pvi.loc[len(df_state_pvi)] = ['Massachusetts', 'D', 12]
df_state_pvi.loc[len(df_state_pvi)] = ['Michigan', 'D', 1]
df_state_pvi.loc[len(df_state_pvi)] = ['Minnesota', 'D', 1]
df_state_pvi.loc[len(df_state_pvi)] = ['Mississippi', 'R', 9]
df_state_pvi.loc[len(df_state_pvi)] = ['Missouri', 'R', 9]
df_state_pvi.loc[len(df_state_pvi)] = ['Montana', 'R', 11]
df_state_pvi.loc[len(df_state_pvi)] = ['Nebraska', 'R', 14]
df_state_pvi.loc[len(df_state_pvi)] = ['Nevada', 'D', 1]
df_state_pvi.loc[len(df_state_pvi)] = ['New Hampshire', 'Even', 0]
df_state_pvi.loc[len(df_state_pvi)] = ['New Jersey', 'D', 7]
df_state_pvi.loc[len(df_state_pvi)] = ['New Mexico', 'D', 3]
df_state_pvi.loc[len(df_state_pvi)] = ['New York', 'D', 12]
df_state_pvi.loc[len(df_state_pvi)] = ['North Carolina', 'R', 3]
df_state_pvi.loc[len(df_state_pvi)] = ['North Dakota', 'R', 17]
df_state_pvi.loc[len(df_state_pvi)] = ['Ohio', 'R', 3]
df_state_pvi.loc[len(df_state_pvi)] = ['Oklahoma', 'R', 20]
df_state_pvi.loc[len(df_state_pvi)] = ['Oregon', 'D', 5]
df_state_pvi.loc[len(df_state_pvi)] = ['Pennsylvania', 'Even', 0]
df_state_pvi.loc[len(df_state_pvi)] = ['Rhode Island', 'D', 10]
df_state_pvi.loc[len(df_state_pvi)] = ['South Carolina', 'R', 8]
df_state_pvi.loc[len(df_state_pvi)] = ['South Dakota', 'R', 14]
df_state_pvi.loc[len(df_state_pvi)] = ['Tennessee', 'R', 14]
df_state_pvi.loc[len(df_state_pvi)] = ['Texas', 'R', 8]
df_state_pvi.loc[len(df_state_pvi)] = ['Utah', 'R', 20]
df_state_pvi.loc[len(df_state_pvi)] = ['Vermont', 'D', 15]
df_state_pvi.loc[len(df_state_pvi)] = ['Virginia', 'D', 1]
df_state_pvi.loc[len(df_state_pvi)] = ['Washington', 'D', 7]
df_state_pvi.loc[len(df_state_pvi)] = ['West Virginia', 'R', 19]
df_state_pvi.loc[len(df_state_pvi)] = ['Wisconsin', 'Even', 0]
df_state_pvi.loc[len(df_state_pvi)] = ['Wyoming', 'R', 25]
df_state_pvi.loc[len(df_state_pvi)] = ['District of Columbia', 'D', 43]
df_state_pvi.loc[len(df_state_pvi)] = ['Guam', 'None', 0]
df_state_pvi.loc[len(df_state_pvi)] = ['Northern Mariana Islands', 'None', 0]
df_state_pvi.loc[len(df_state_pvi)] = ['Puerto Rico', 'None', 0]
df_state_pvi.loc[len(df_state_pvi)] = ['Virgin Islands', 'None', 0]

def color_pvi(df_state_pvi, state):

    palette_D = plt.get_cmap('Blues')
    palette_R = plt.get_cmap('Reds')
    palette_Even = plt.get_cmap('Greens')
    palette_None = plt.get_cmap('Purples')

    D_max = max(df_state_pvi[df_state_pvi['partisan'] == 'D']['value'])
    D_min = min(df_state_pvi[df_state_pvi['partisan'] == 'D']['value'])
    R_max = max(df_state_pvi[df_state_pvi['partisan'] == 'R']['value'])
    R_min = min(df_state_pvi[df_state_pvi['partisan'] == 'R']['value'])

    partisan = df_state_pvi[df_state_pvi['state_name'] == state]['partisan'].tolist()[0]
    value = df_state_pvi[df_state_pvi['state_name'] == state]['value'].tolist()[0]
    if partisan == 'Even':
        return palette_Even(0.6)
    elif partisan == 'None':
        return palette_None(0.6)
    elif partisan == 'D':
        return palette_D((value - D_min)/2/(D_max - D_min) + 0.3)
    else:
        return palette_R((value - R_min)/2/(R_max - R_min) + 0.3)
```

```python
# Stock market data
# S & P 500 index
def load_SP_raw():
    '''
    This provides a way to lookinto the 'raw' data
    '''
    SP = yf.Ticker("^GSPC")
    raw = SP.history(period="max")
    raw = raw.reset_index()
    rename_dict = {'Date': 'update_date',
                   'Open': 'open',
                   'High': 'high',
                   'Low': 'low',
                   'Close': 'close',
                   'Volume': 'volume',
                   'Dividends': 'dividends',
                   'Stock Splits': 'stock splits'
                  }
    data = raw.rename(columns=rename_dict)
    data['update_date'] = pd.to_datetime(data['update_date'])  # original type of update_time after read_csv is 'str'
    data['update_date'] = data['update_date'].dt.date
    data = data.reset_index(drop=True)
    print('Data date range: ', data['update_date'].min(), 'to', data['update_date'].max())
    print('Number of rows in raw data: ', data.shape[0])
    return data
# Stock price of a certain cooperation
def load_STOCK_raw(name):
    '''
    This provides a way to lookinto the 'raw' data
    '''
    SP = yf.Ticker(name)
    raw = SP.history(period="max")
    raw = raw.reset_index()
    rename_dict = {'Date': 'update_date',
                   'Open': 'open',
                   'High': 'high',
                   'Low': 'low',
                   'Close': 'close',
                   'Volume': 'volume',
                   'Dividends': 'dividends',
                   'Stock Splits': 'stock splits'
                  }
    data = raw.rename(columns=rename_dict)
    data['update_date'] = pd.to_datetime(data['update_date'])  # original data type of update_time is 'str'
    data['update_date'] = data['update_date'].dt.date
    data = data.reset_index(drop=True)
    #print('Data date range: ', data['update_date'].min(), 'to', data['update_date'].max())
    #print('Number of rows in raw data: ', data.shape[0])
    return data
```

```python
armyrose = ['#798234', '#a3ad62', '#d0d3a2', '#fdfbe4', '#f0c6c3', '#df91a3', '#d46780']
tealrose = ['#009392', '#72aaa1', '#b1c7b3', '#f1eac8', '#e5b9ad', '#d98994', '#d0587e']
geyser = ['#008080', '#70a494', '#b4c8a8', '#f6edbd', '#edbb8a', '#de8a5a', '#ca562c']
earth = ['#A16928', '#bd925a', '#d6bd8d', '#edeac2', '#b5c8b8', '#79a7ac', '#2887a1']
earth.reverse()
fall = ['#3d5941', '#778868', '#b5b991', '#f6edbd', '#edbb8a', '#de8a5a', '#ca562c']

# hex color code to rgb color code
def hex_to_rgb(value):
    value = value.lstrip('#')
    lv = len(value)
    rgb_int = tuple(int(value[i:i+lv//3], 16) for i in range(0, lv, lv//3))
    rgb_str = 'rgb(' + str(rgb_int[0]) + ',' + str(rgb_int[1]) + ',' + str(rgb_int[2]) + ')'
    return rgb_str
```

```python
# ODE systems
#%%latex
#Difference equations
#without immigration
#\begin{align}
#S(t + 1) - S(t) &= -\beta(t)S(t)\frac{I(t)}{N(t)}, \\
#E(t + 1) - E(t) &= \beta(t)S(t)\frac{I(t)}{N(t)} - \sigma(t)E(t), \\
#I(t + 1) - I(t) &= \sigma(t)E(t) - \gamma(t)I(t), \\
#R(t + 1) - R(t) &= \gamma(t)I(t)
#\end{align}
#Difference equations
#with immigration
#\begin{align}
#S_i(t + 1) - S_i(t) &= -\beta_i(t)S_i(t)\frac{I_i(t)}{N_i(t)} - \sum_{j, j \neq i}a_{ij}(t)S_i(t) + \sum_{j, j \neq i}a_{ji}(t)S_j(t) \\
#E_i(t + 1) - E_i(t) &= \beta_i(t)S_i(t)\frac{I_i(t)}{N_i(t)} - \sigma_i(t)E_i(t) - \sum_{j, j \neq i}a_{ij}(t)E_i(t) + \sum_{j, j \neq i}a_{ji}(t)E_j(t) \\
#I_i(t + 1) - I_i(t) &= \sigma_i(t)E_i(t) - \gamma_i(t)I_i(t) \\
#R_i(t + 1) - R_i(t) &= \gamma_i(t)I_i(t)
#\end{align}
```


```python
from pathlib import Path
import sys
```

```python
sys.version
```

    '3.8.0 (default, Oct 28 2019, 16:14:01) \n[GCC 8.3.0]'

```python
sys.path.insert(0, str(Path.cwd().parent.parent))
```

```python
cd ../
```

    /home/fuzzy/projects/py38/ncv

```python
from py.startup import *
```

```python
ls()
```

    data  etc  json  log  ncv  notes  run  setup.py  test  tests

```python
from configparser import ConfigParser
from enum import Enum, IntEnum

from py.charconsts import *

WorkCoordinates = int

class NonModal(IntEnum):
    DWELL = 4
    EXACT_STOP = 9
    SET_OFFSETS = 10
    CLOCKWISE_CIRCULAR_POCKET_MILLING = 12
    COUNTER_CLOCKWISE_CIRCULAR_POCKET_MILLING = 13
    RETURN_2_REF_PT = 28
    SET_RETURN_THRU_REF_PT = 29
    FEED_UNTIL_SKIP = 31 # optional
    AUTO_TOOL_DIAMETER_MEASURE = 35 # optional
    AUTO_WORK_OFFSET_MEASURE = 36 # optional
    AUTO_TOOL_OFFSET_MEASURE = 37 # optional
    TEXT_ENGRAVING = 47
    SET_LOCAL_SHIFT_COORDINATE_SYSTEM = 52 # fanuc
    NON_MODAL_MACHINE_COORDINATE_SELECTION = 53
    UNIDIRECTIONAL_POSITIONING = 60
    MACRO_SUBROUTINE_CALL = 65 # optional
    BOLT_HOLE_CIRCLE = 70 # yasnac
    BOLT_HOLE_ARC = 71 # yasnac
    BOLT_HOLES_ANGLE = 72
    SET_WORK_COORDINATES = 92
    CANCEL_MIRROR_IMAGE = 100
    ENABLE_MIRROR_IMAGE = 101
    PROGRAMMABLE_OUTPUT = 102
    LIMIT_BLOCK_BUFFERING = 103
    CYLINDRICAL_MAPPING = 107
    AUTOMATIC_WORK_OFFSET_CENTER_MEASURE = 136
    GENERAL_PURPOSE_POCKET_MILLING = 150
    SPECIAL_PURPOSE_RIGID_TAPPING_COUNTER_CLOCKWISE = 174
    SPECIAL_PURPOSE_RIGIT_TAPPING_CLOCKWISE = 184
    ACCURACY_CONTROL = 187

class MotionMode(IntEnum):
    RAPID = 0
    LINEAR = 1
    CLOCKWISE = 2
    COUNTER = 3
    POCKET = 12
    COUNTER_POCKET = 13

class PositionMode(IntEnum):
    ABSOLUTE = 90
    INCREMENTAL = 91

class CutterOffsetMode(IntEnum):
    OFF   = 40
    LEFT  = 41
    RIGHT = 42

class CannedCycle(IntEnum):
    NONE = 0
    CANCEL = 80
    DRILL = 81
    SPOT = 82
    PECK = 83
    TAP = 84
    BORE = 85
    IN_OUT = 86
    RETRACT = 87
    MANUAL = 88
    DWELL = 89

class CircularPlane(IntEnum):
    XY = 17
    ZX = 18
    YZ = 19

class Units(IntEnum):
    INCHES = 20
    METRIC = 21

class ToolOffsetMode(IntEnum):
    POSITIVE = 43
    NEGATIVE = 44
    OFF = 49

class ExactStopModal(IntEnum):
    ON = 61
    OFF = 64

class FeedMode(IntEnum):
    INVERSE_TIME = 93
    PER_MINUTE = 94

class ReturnMode(IntEnum):
    INITIAL_POINT = 98
    R_PLANE = 99

class MirrorImage(IntEnum):
    CANCEL = 100
    ENABLE = 101

class SpindleState(IntEnum):
    OFF = 0
    FORWARD = 1
    REVERSE = 2

class ScalingMode(IntEnum):
    OFF = 50
    ON = 51

class Rotation(IntEnum):
    ON = 68
    OFF = 69

class GCodeGroup(Enum):
    NON_MODAL = NonModal
    MOTION = MotionMode
    PLANE = CircularPlane
    POSITION_MODE = PositionMode
    FEED_MODE = FeedMode
    UNITS = Units
    CUTTER_COMPENSATION = CutterOffsetMode
    TOOL_LENGTH = ToolOffsetMode
    CANNED_CYCLE = CannedCycle
    RETURN = ReturnMode
    SCALING = ScalingMode
    WORK_COORDINATES = WorkCoordinates
    EXACT_STOP_MODAL = ExactStopModal
    ROTATION = Rotation

gCodeClassList = [
    NonModal, MotionMode, CircularPlane, PositionMode, FeedMode, Units, CutterOffsetMode, ToolOffsetMode,
    CannedCycle, ReturnMode, ScalingMode, WorkCoordinates, ExactStopModal, Rotation
]

gCodeGroups = {
    GCodeGroup.NON_MODAL : [ 4, 9, 10, 12, 13, 28, 29, 32, *range(35, 38),
                             47, 52, 53, 60, 65, *range(70, 73), 92,
                             *range(100, 104), 107, 136, 150, 174, 184, 187
                           ],
    GCodeGroup.MOTION : [ *range(0, 4), ],
    GCodeGroup.PLANE : [ *range(17, 20)],
    GCodeGroup.POSITION_MODE : [ 90, 91 ],
    GCodeGroup.FEED_MODE : [ 93, 94 ],
    GCodeGroup.UNITS : [ 20, 21 ],
    GCodeGroup.CUTTER_COMPENSATION : [ *range(40, 43), 141 ],
    GCodeGroup.TOOL_LENGTH : [ 43, 44, 49, 143 ],
    GCodeGroup.CANNED_CYCLE : [ *range(73, 90) ],
    GCodeGroup.RETURN : [ 98, 99 ],
    GCodeGroup.SCALING : [ 50, 51 ],
    GCodeGroup.WORK_COORDINATES : [ *range(54, 60), *range(100, 130) ],
    GCodeGroup.EXACT_STOP_MODAL : [ 61, 64 ],
    GCodeGroup.ROTATION : [ 68, 69 ]
}

gCodeGroupsDict = dict(zip(gCodeGroups.keys(), gCodeClassList))

```

```python
from collections import ChainMap
from pprint import pformat, pprint as pp

from nested_dict import nested_dict
from sympy import Point3D
Point = Point3D

DEFAULT_GCODES = [ list(), MotionMode.RAPID, CircularPlane.XY, PositionMode.ABSOLUTE, FeedMode.PER_MINUTE,
                   Units.INCHES, CutterOffsetMode.OFF, ToolOffsetMode.NEGATIVE, CannedCycle.CANCEL,
                   ReturnMode.INITIAL_POINT, False, 54, ExactStopModal.OFF, False ]

DEFAULT_VARS = {
#     "current_position" : Point(),
    "A" : 0.0,
    "B" : 0.0,
    "C" : 0.0,
    "D" : 0,
    "E" : 0.0001, # minimum; 0.25 is the max
    "F" : 0.0,
    'G' : dict(zip(gCodeClassList, DEFAULT_GCODES)),
    'H' : 0,
    "I" : 0.0,
    "J" : 0.0,
    "K" : 0.0,
    "L" : 0.0,
    "M" : None,
    "N" : None,
    "O" : 0,
    "P" : 0.0,
    "Q" : 0.0,
    "R" : 0.0,
    "S" : 1,
    "T" : 0,
    "U" : 0.0001,
    "V" : 0.0001,
    "W" : 0.0001,
    "X" : 0.0,
    "Y" : 0.0,
    "Z" : 0.0,

}

CONFIG_FILE = 'etc/config.ini'
ARGS_FILE = 'json/arguments.json'
CONFIG = ConfigParser()
CONFIG.read(CONFIG_FILE)

DEFAULT_VARS = dict(ChainMap(dict(CONFIG["ENVIRONMENT"]), DEFAULT_VARS))

class Environment(ChainMap):
    WORK_COORDS_RANGE = [ *range(53,60), *range(110, 130) ]
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
#         self.update(DEFAULT_VARS)
#         self.workCoords = dict()
#         for i in self.WORK_COORDS_RANGE:
#             self.workCoords[i] = Point()

#     def get_work_coords(self, i):
#         if i in self.WORK_COORDS_RANGE:
#             return self.workCoords[i]
#         else:
#             return None

#     def update(self, *args, **kwargs):
#         print("Updating the environment...")
#         pp(args)
#         pp("`dict` argument to `env.update()`:")
#         print(pformat(args[0]))
#         gcodes = args[0].pop('G')
#         if 'G' in self.keys():
#             gcodes.update(self['G'])
#         else:
#             self['G'] = gcodes
#         return super().update(*args, **kwargs)

```

```python
class Word(object):
    """
    """
    def __init__(self, s):
        self.cmd = s[0].upper()
        self.param = int(s[1:].rstrip(';')) if not '.' in s[1:] else float(s[1:].rstrip(';'))

    def __repr__(self):
        return self.cmd + str(self.param)

    def __str__(self):

        return self.__repr__()

    def execute(self):
        return { self.cmd : self.param }

PRIORITY_LIST = [ 'O', 'G', 'A', 'B', 'C', 'I', 'J', 'K', 'L', 'P', 'Q', 'X', 'Y', 'Z',
                  'H', 'D', 'E', 'F', 'R', 'S', 'T', 'U', 'V', 'W', 'M', 'N'
                ]

class Block(list):
    """
    """
    def __init__(self, line):
        super().__init__()
        for word in line.partition(SEMICOLON)[0].split():
            word = word.upper()
            for CODE in PRIORITY_LIST:
                if word[0] == CODE:
                    self.append(Word(word))

    def __repr__(self):
        """
        """
        return ' '.join([str(s) for s in self]) + ';'

    def __str__(self):
        """
        """
        return self.__repr__()

    def pop_G_codes(self):
        result = list()
        for word in self:
            if word.cmd == 'G':
                result.append(word.param)
                self.remove(word)
        return result

    def execute(self):
        """
        """
        print("Executing block: ", self)
        result = dict()
        gcodes = list()
#         mcode = None
        for word in self:
            # TODO: Deal with G codes here (?)
            if word.cmd == 'G':
                gcodes.append(word.param)
#             elif cmd.cmd == 'M'
            else:
                result.update(word.execute())
#         if gcodes:
#             result.update({"G" : self.make_gcodes_dict(gcodes)})
        return result

    def mCode(self):
        for w in self[-1:]:
            if w.cmd == 'M':
                return w.param
        return None

class NCProgram(list):
    """
    """
    class State(Enum):
        BEGIN = 0
        PAUSED = 1
        RUNNING = 2
        END = 3

    def __init__(self, lines):
        super(list, self).__init__()
        if type(lines) is str:
            lines = lines.split(NEWLINE)
        for line in lines:
            self.append(Block(line))

    # def run(self):
    #     while self.state != self.State.END:
    #         self.step()

    def step(self):
        """
        """
        if self.current_block >= len(self):
            self.state = self.State.END
        else:
            return self[self.current_block].execute()
            self.current_block += 1

```

```python
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d
plt.ion()
```

```python
class NCViewer():
    def __init__(self, settings=None):
        self.settings = settings
        if self.settings["verbose"]:
            print()
            print("Settings:")
            print()
            print(pformat([f"{k}: {v}" for k, v in settings.items()]).lstrip('[').rstrip(']'))
            print()
        self.env = Environment(DEFAULT_VARS)
        # programs should be loaded from a directory located by a configuration file setting
        self.programs = list()
        self.fig = plt.figure()
        self.ax = self.fig.add_subplot(111, projection='3d')
        # self.ax.plot([0.0, 1.0], [0.2, 0.7], 0.4)
        self.current_position = Point()
        plt.show()

    def code_group(self, n):
        for k in gCodeGroups.keys():
            if n in gCodeGroups[k]:
                return gCodeGroupsDict[k]
        return None

    def make_gcodes_dict(self, gcodes):
        """
        """
        result = dict()
        for n in gcodes:
            klass = self.code_group(n)
            result.update({klass : klass(n)})
        return result

    def execute(self, blk):
        result = dict()
        # validate the block and exit on error
        # extract the `G` codes
        result = self.env["G"]
        print()
        print("Current G Code Settings:")
        print()
        pp(result)
        result.update(self.make_gcodes_dict(blk.pop_G_codes()))
        result = { "G" : result }
        print()
        print("New G Code Settings:")
        print()
        pp(result)
        print()
        # process the `G` codes
        # extract the `M` code
#         result.update({"G" : self.make_gcodes_dict(gcodes)})
        result.update(blk.execute())
        print("New Result Environment:")
        print()
        pp(result)
        print()
        self.env = self.env.new_child(result)
        print(self.env.maps[-1])
        self.move()
        if blk.mCode():
            pass
            # process the `M` code

    def do_step(self):
        if self.current_program:
            self.env = self.env.new_child(self.current_program.step())
            return self.move()
        else:
            print("ERROR: step: No program is loaded.")
            return False

    def move(self):
        P = self.current_position
        if self.settings["verbose"]:
            print("Current position:", P)
        Q = Point(*[self.env["X"], self.env["Y"], self.env["Z"]])
        if self.settings["verbose"]:
            print("X, Y, Z:", Q)
        if self.env["G"][PositionMode] == PositionMode.INCREMENTAL:
            Q = P + Q
        if P != Q:
            print("Moving the tool...")
            print("Motion mode:", self.env["G"][MotionMode])
#             print("RAPID:", MotionMode.RAPID)
#             print("LINEAR:", MotionMode.LINEAR)
            if self.env["G"][MotionMode] in { MotionMode.RAPID, MotionMode.LINEAR }:
                print("Motion mode:", self.env["G"][MotionMode])
                self.ax.plot([P.x, Q.x], [P.y, Q.y], [P.z, Q.z])
        self.current_position = Q
        return True

    def do_run(self):
        while self.current_program.state != NCProgram.State.END and self.do_step():
            pass

    def is_valid_gcode(self, s):
        return True

    def default(self, args):
        if self.is_valid_gcode(args): self.execute(Block(args))
        else:
            super().default(args)
        # self.ax.plot([1.0, 3.0], [0.2, 0.7], 0.4)

```

```python
SETTINGS = ChainMap({'args': [], 'debug': False, 'verbose': False, 'recursive': False, 'testing': False} )
```

```python
ncp = NCViewer(SETTINGS)
```

```python
ncp.default("X1.0")
plt.show()
```

    Current G Code Settings:

    {<enum 'PositionMode'>: <PositionMode.ABSOLUTE: 90>,
     <enum 'CutterOffsetMode'>: <CutterOffsetMode.OFF: 40>,
     <enum 'NonModal'>: [],
     <enum 'CannedCycle'>: <CannedCycle.CANCEL: 80>,
     <enum 'CircularPlane'>: <CircularPlane.XY: 17>,
     <enum 'Units'>: <Units.INCHES: 20>,
     <enum 'ToolOffsetMode'>: <ToolOffsetMode.NEGATIVE: 44>,
     <enum 'ExactStopModal'>: <ExactStopModal.OFF: 64>,
     <enum 'FeedMode'>: <FeedMode.PER_MINUTE: 94>,
     <enum 'ReturnMode'>: <ReturnMode.INITIAL_POINT: 98>,
     <enum 'ScalingMode'>: False,
     <enum 'Rotation'>: False,
     <enum 'MotionMode'>: <MotionMode.RAPID: 0>,
     <class 'int'>: 54}

    New G Code Settings:

    {'G': {<enum 'PositionMode'>: <PositionMode.ABSOLUTE: 90>,
           <enum 'CutterOffsetMode'>: <CutterOffsetMode.OFF: 40>,
           <enum 'NonModal'>: [],
           <enum 'CannedCycle'>: <CannedCycle.CANCEL: 80>,
           <enum 'CircularPlane'>: <CircularPlane.XY: 17>,
           <enum 'Units'>: <Units.INCHES: 20>,
           <enum 'ToolOffsetMode'>: <ToolOffsetMode.NEGATIVE: 44>,
           <enum 'ExactStopModal'>: <ExactStopModal.OFF: 64>,
           <enum 'FeedMode'>: <FeedMode.PER_MINUTE: 94>,
           <enum 'ReturnMode'>: <ReturnMode.INITIAL_POINT: 98>,
           <enum 'ScalingMode'>: False,
           <enum 'Rotation'>: False,
           <enum 'MotionMode'>: <MotionMode.RAPID: 0>,
           <class 'int'>: 54}}

    Executing block:  X1.0;
    New Result Environment:

    {'G': {<enum 'PositionMode'>: <PositionMode.ABSOLUTE: 90>,
           <enum 'CutterOffsetMode'>: <CutterOffsetMode.OFF: 40>,
           <enum 'NonModal'>: [],
           <enum 'CannedCycle'>: <CannedCycle.CANCEL: 80>,
           <enum 'CircularPlane'>: <CircularPlane.XY: 17>,
           <enum 'Units'>: <Units.INCHES: 20>,
           <enum 'ToolOffsetMode'>: <ToolOffsetMode.NEGATIVE: 44>,
           <enum 'ExactStopModal'>: <ExactStopModal.OFF: 64>,
           <enum 'FeedMode'>: <FeedMode.PER_MINUTE: 94>,
           <enum 'ReturnMode'>: <ReturnMode.INITIAL_POINT: 98>,
           <enum 'ScalingMode'>: False,
           <enum 'Rotation'>: False,
           <enum 'MotionMode'>: <MotionMode.RAPID: 0>,
           <class 'int'>: 54},
     'X': 1.0}

    {'A': 0.0, 'B': 0.0, 'C': 0.0, 'D': 0, 'E': 0.0001, 'F': 0.0, 'G': {<enum 'NonModal'>: [], <enum 'MotionMode'>: <MotionMode.RAPID: 0>, <enum 'CircularPlane'>: <CircularPlane.XY: 17>, <enum 'PositionMode'>: <PositionMode.ABSOLUTE: 90>, <enum 'FeedMode'>: <FeedMode.PER_MINUTE: 94>, <enum 'Units'>: <Units.INCHES: 20>, <enum 'CutterOffsetMode'>: <CutterOffsetMode.OFF: 40>, <enum 'ToolOffsetMode'>: <ToolOffsetMode.NEGATIVE: 44>, <enum 'CannedCycle'>: <CannedCycle.CANCEL: 80>, <enum 'ReturnMode'>: <ReturnMode.INITIAL_POINT: 98>, <enum 'ScalingMode'>: False, <class 'int'>: 54, <enum 'ExactStopModal'>: <ExactStopModal.OFF: 64>, <enum 'Rotation'>: False}, 'H': 0, 'I': 0.0, 'J': 0.0, 'K': 0.0, 'L': 0.0, 'M': None, 'N': None, 'O': 0, 'P': 0.0, 'Q': 0.0, 'R': 0.0, 'S': 1, 'T': 0, 'U': 0.0001, 'V': 0.0001, 'W': 0.0001, 'X': 0.0, 'Y': 0.0, 'Z': 0.0, 'program': 'ncv', 'version': '0.0.0', 'packages_dir': 'projects/py38'}
    Moving the tool...
    Motion mode: MotionMode.RAPID
    Motion mode: MotionMode.RAPID

```python
ncp.default("G1 Y2.0")
```

    Current G Code Settings:

    {<enum 'PositionMode'>: <PositionMode.ABSOLUTE: 90>,
     <enum 'CutterOffsetMode'>: <CutterOffsetMode.OFF: 40>,
     <enum 'NonModal'>: [],
     <enum 'CannedCycle'>: <CannedCycle.CANCEL: 80>,
     <enum 'CircularPlane'>: <CircularPlane.XY: 17>,
     <enum 'Units'>: <Units.INCHES: 20>,
     <enum 'ToolOffsetMode'>: <ToolOffsetMode.NEGATIVE: 44>,
     <enum 'ExactStopModal'>: <ExactStopModal.OFF: 64>,
     <enum 'FeedMode'>: <FeedMode.PER_MINUTE: 94>,
     <enum 'ReturnMode'>: <ReturnMode.INITIAL_POINT: 98>,
     <enum 'ScalingMode'>: False,
     <enum 'Rotation'>: False,
     <enum 'MotionMode'>: <MotionMode.RAPID: 0>,
     <class 'int'>: 54}

    New G Code Settings:

    {'G': {<enum 'PositionMode'>: <PositionMode.ABSOLUTE: 90>,
           <enum 'CutterOffsetMode'>: <CutterOffsetMode.OFF: 40>,
           <enum 'NonModal'>: [],
           <enum 'CannedCycle'>: <CannedCycle.CANCEL: 80>,
           <enum 'CircularPlane'>: <CircularPlane.XY: 17>,
           <enum 'Units'>: <Units.INCHES: 20>,
           <enum 'ToolOffsetMode'>: <ToolOffsetMode.NEGATIVE: 44>,
           <enum 'ExactStopModal'>: <ExactStopModal.OFF: 64>,
           <enum 'FeedMode'>: <FeedMode.PER_MINUTE: 94>,
           <enum 'ReturnMode'>: <ReturnMode.INITIAL_POINT: 98>,
           <enum 'ScalingMode'>: False,
           <enum 'Rotation'>: False,
           <enum 'MotionMode'>: <MotionMode.LINEAR: 1>,
           <class 'int'>: 54}}

    Executing block:  Y2.0;
    New Result Environment:

    {'G': {<enum 'PositionMode'>: <PositionMode.ABSOLUTE: 90>,
           <enum 'CutterOffsetMode'>: <CutterOffsetMode.OFF: 40>,
           <enum 'NonModal'>: [],
           <enum 'CannedCycle'>: <CannedCycle.CANCEL: 80>,
           <enum 'CircularPlane'>: <CircularPlane.XY: 17>,
           <enum 'Units'>: <Units.INCHES: 20>,
           <enum 'ToolOffsetMode'>: <ToolOffsetMode.NEGATIVE: 44>,
           <enum 'ExactStopModal'>: <ExactStopModal.OFF: 64>,
           <enum 'FeedMode'>: <FeedMode.PER_MINUTE: 94>,
           <enum 'ReturnMode'>: <ReturnMode.INITIAL_POINT: 98>,
           <enum 'ScalingMode'>: False,
           <enum 'Rotation'>: False,
           <enum 'MotionMode'>: <MotionMode.LINEAR: 1>,
           <class 'int'>: 54},
     'Y': 2.0}

    {'A': 0.0, 'B': 0.0, 'C': 0.0, 'D': 0, 'E': 0.0001, 'F': 0.0, 'G': {<enum 'NonModal'>: [], <enum 'MotionMode'>: <MotionMode.LINEAR: 1>, <enum 'CircularPlane'>: <CircularPlane.XY: 17>, <enum 'PositionMode'>: <PositionMode.ABSOLUTE: 90>, <enum 'FeedMode'>: <FeedMode.PER_MINUTE: 94>, <enum 'Units'>: <Units.INCHES: 20>, <enum 'CutterOffsetMode'>: <CutterOffsetMode.OFF: 40>, <enum 'ToolOffsetMode'>: <ToolOffsetMode.NEGATIVE: 44>, <enum 'CannedCycle'>: <CannedCycle.CANCEL: 80>, <enum 'ReturnMode'>: <ReturnMode.INITIAL_POINT: 98>, <enum 'ScalingMode'>: False, <class 'int'>: 54, <enum 'ExactStopModal'>: <ExactStopModal.OFF: 64>, <enum 'Rotation'>: False}, 'H': 0, 'I': 0.0, 'J': 0.0, 'K': 0.0, 'L': 0.0, 'M': None, 'N': None, 'O': 0, 'P': 0.0, 'Q': 0.0, 'R': 0.0, 'S': 1, 'T': 0, 'U': 0.0001, 'V': 0.0001, 'W': 0.0001, 'X': 0.0, 'Y': 0.0, 'Z': 0.0, 'program': 'ncv', 'version': '0.0.0', 'packages_dir': 'projects/py38'}
    Moving the tool...
    Motion mode: MotionMode.LINEAR
    Motion mode: MotionMode.LINEAR

```python
ncp.env
```

    Environment({'G': {<enum 'NonModal'>: [], <enum 'MotionMode'>: <MotionMode.LINEAR: 1>, <enum 'CircularPlane'>: <CircularPlane.XY: 17>, <enum 'PositionMode'>: <PositionMode.ABSOLUTE: 90>, <enum 'FeedMode'>: <FeedMode.PER_MINUTE: 94>, <enum 'Units'>: <Units.INCHES: 20>, <enum 'CutterOffsetMode'>: <CutterOffsetMode.OFF: 40>, <enum 'ToolOffsetMode'>: <ToolOffsetMode.NEGATIVE: 44>, <enum 'CannedCycle'>: <CannedCycle.CANCEL: 80>, <enum 'ReturnMode'>: <ReturnMode.INITIAL_POINT: 98>, <enum 'ScalingMode'>: False, <class 'int'>: 54, <enum 'ExactStopModal'>: <ExactStopModal.OFF: 64>, <enum 'Rotation'>: False}, 'Y': 2.0}, {'G': {<enum 'NonModal'>: [], <enum 'MotionMode'>: <MotionMode.LINEAR: 1>, <enum 'CircularPlane'>: <CircularPlane.XY: 17>, <enum 'PositionMode'>: <PositionMode.ABSOLUTE: 90>, <enum 'FeedMode'>: <FeedMode.PER_MINUTE: 94>, <enum 'Units'>: <Units.INCHES: 20>, <enum 'CutterOffsetMode'>: <CutterOffsetMode.OFF: 40>, <enum 'ToolOffsetMode'>: <ToolOffsetMode.NEGATIVE: 44>, <enum 'CannedCycle'>: <CannedCycle.CANCEL: 80>, <enum 'ReturnMode'>: <ReturnMode.INITIAL_POINT: 98>, <enum 'ScalingMode'>: False, <class 'int'>: 54, <enum 'ExactStopModal'>: <ExactStopModal.OFF: 64>, <enum 'Rotation'>: False}, 'X': 1.0}, {'A': 0.0, 'B': 0.0, 'C': 0.0, 'D': 0, 'E': 0.0001, 'F': 0.0, 'G': {<enum 'NonModal'>: [], <enum 'MotionMode'>: <MotionMode.LINEAR: 1>, <enum 'CircularPlane'>: <CircularPlane.XY: 17>, <enum 'PositionMode'>: <PositionMode.ABSOLUTE: 90>, <enum 'FeedMode'>: <FeedMode.PER_MINUTE: 94>, <enum 'Units'>: <Units.INCHES: 20>, <enum 'CutterOffsetMode'>: <CutterOffsetMode.OFF: 40>, <enum 'ToolOffsetMode'>: <ToolOffsetMode.NEGATIVE: 44>, <enum 'CannedCycle'>: <CannedCycle.CANCEL: 80>, <enum 'ReturnMode'>: <ReturnMode.INITIAL_POINT: 98>, <enum 'ScalingMode'>: False, <class 'int'>: 54, <enum 'ExactStopModal'>: <ExactStopModal.OFF: 64>, <enum 'Rotation'>: False}, 'H': 0, 'I': 0.0, 'J': 0.0, 'K': 0.0, 'L': 0.0, 'M': None, 'N': None, 'O': 0, 'P': 0.0, 'Q': 0.0, 'R': 0.0, 'S': 1, 'T': 0, 'U': 0.0001, 'V': 0.0001, 'W': 0.0001, 'X': 0.0, 'Y': 0.0, 'Z': 0.0, 'program': 'ncv', 'version': '0.0.0', 'packages_dir': 'projects/py38'})

```python
help(ncp.env.new_child)
```

    Help on method new_child in module collections:

    new_child(m=None) method of __main__.Environment instance
        New ChainMap with a new map followed by all previous maps.
        If no map is provided, an empty dict is used.

```python
GCodeGroup.__members__.values()
```

    dict_values([<GCodeGroup.NON_MODAL: <enum 'NonModal'>>, <GCodeGroup.MOTION: <enum 'MotionMode'>>, <GCodeGroup.PLANE: <enum 'CircularPlane'>>, <GCodeGroup.POSITION_MODE: <enum 'PositionMode'>>, <GCodeGroup.FEED_MODE: <enum 'FeedMode'>>, <GCodeGroup.UNITS: <enum 'Units'>>, <GCodeGroup.CUTTER_COMPENSATION: <enum 'CutterOffsetMode'>>, <GCodeGroup.TOOL_LENGTH: <enum 'ToolOffsetMode'>>, <GCodeGroup.CANNED_CYCLE: <enum 'CannedCycle'>>, <GCodeGroup.RETURN: <enum 'ReturnMode'>>, <GCodeGroup.SCALING: <enum 'ScalingMode'>>, <GCodeGroup.WORK_COORDINATES: <class 'int'>>, <GCodeGroup.EXACT_STOP_MODAL: <enum 'ExactStopModal'>>, <GCodeGroup.ROTATION: <enum 'Rotation'>>])

```python
ncp.make_gcodes_dict([0,17,92,116])
```

    {<enum 'MotionMode'>: <MotionMode.RAPID: 0>,
     <enum 'CircularPlane'>: <CircularPlane.XY: 17>,
     <enum 'NonModal'>: <NonModal.SET_WORK_COORDINATES: 92>,
     int: 116}

```python
92 == NonModal.SET_WORK_COORDINATES
```

    True

```python
17 in CircularPlane.__members__.values()
```

    True

```python
ncp.default("G00 X0.1 Y0.2 Z0.3 F14.0")
```

    Current G Code Settings:

    {<enum 'PositionMode'>: <PositionMode.ABSOLUTE: 90>,
     <enum 'CutterOffsetMode'>: <CutterOffsetMode.OFF: 40>,
     <enum 'NonModal'>: [],
     <enum 'CannedCycle'>: <CannedCycle.CANCEL: 80>,
     <enum 'CircularPlane'>: <CircularPlane.XY: 17>,
     <enum 'Units'>: <Units.INCHES: 20>,
     <enum 'ToolOffsetMode'>: <ToolOffsetMode.NEGATIVE: 44>,
     <enum 'ExactStopModal'>: <ExactStopModal.OFF: 64>,
     <enum 'FeedMode'>: <FeedMode.PER_MINUTE: 94>,
     <enum 'ReturnMode'>: <ReturnMode.INITIAL_POINT: 98>,
     <enum 'ScalingMode'>: False,
     <enum 'Rotation'>: False,
     <enum 'MotionMode'>: <MotionMode.LINEAR: 1>,
     <class 'int'>: 54}

    New G Code Settings:

    {'G': {<enum 'PositionMode'>: <PositionMode.ABSOLUTE: 90>,
           <enum 'CutterOffsetMode'>: <CutterOffsetMode.OFF: 40>,
           <enum 'NonModal'>: [],
           <enum 'CannedCycle'>: <CannedCycle.CANCEL: 80>,
           <enum 'CircularPlane'>: <CircularPlane.XY: 17>,
           <enum 'Units'>: <Units.INCHES: 20>,
           <enum 'ToolOffsetMode'>: <ToolOffsetMode.NEGATIVE: 44>,
           <enum 'ExactStopModal'>: <ExactStopModal.OFF: 64>,
           <enum 'FeedMode'>: <FeedMode.PER_MINUTE: 94>,
           <enum 'ReturnMode'>: <ReturnMode.INITIAL_POINT: 98>,
           <enum 'ScalingMode'>: False,
           <enum 'Rotation'>: False,
           <enum 'MotionMode'>: <MotionMode.RAPID: 0>,
           <class 'int'>: 54}}

    Executing block:  X0.1 Y0.2 Z0.3 F14.0;
    New Result Environment:

    {'F': 14.0,
     'G': {<enum 'PositionMode'>: <PositionMode.ABSOLUTE: 90>,
           <enum 'CutterOffsetMode'>: <CutterOffsetMode.OFF: 40>,
           <enum 'NonModal'>: [],
           <enum 'CannedCycle'>: <CannedCycle.CANCEL: 80>,
           <enum 'CircularPlane'>: <CircularPlane.XY: 17>,
           <enum 'Units'>: <Units.INCHES: 20>,
           <enum 'ToolOffsetMode'>: <ToolOffsetMode.NEGATIVE: 44>,
           <enum 'ExactStopModal'>: <ExactStopModal.OFF: 64>,
           <enum 'FeedMode'>: <FeedMode.PER_MINUTE: 94>,
           <enum 'ReturnMode'>: <ReturnMode.INITIAL_POINT: 98>,
           <enum 'ScalingMode'>: False,
           <enum 'Rotation'>: False,
           <enum 'MotionMode'>: <MotionMode.RAPID: 0>,
           <class 'int'>: 54},
     'X': 0.1,
     'Y': 0.2,
     'Z': 0.3}

    {'A': 0.0, 'B': 0.0, 'C': 0.0, 'D': 0, 'E': 0.0001, 'F': 0.0, 'G': {<enum 'NonModal'>: [], <enum 'MotionMode'>: <MotionMode.RAPID: 0>, <enum 'CircularPlane'>: <CircularPlane.XY: 17>, <enum 'PositionMode'>: <PositionMode.ABSOLUTE: 90>, <enum 'FeedMode'>: <FeedMode.PER_MINUTE: 94>, <enum 'Units'>: <Units.INCHES: 20>, <enum 'CutterOffsetMode'>: <CutterOffsetMode.OFF: 40>, <enum 'ToolOffsetMode'>: <ToolOffsetMode.NEGATIVE: 44>, <enum 'CannedCycle'>: <CannedCycle.CANCEL: 80>, <enum 'ReturnMode'>: <ReturnMode.INITIAL_POINT: 98>, <enum 'ScalingMode'>: False, <class 'int'>: 54, <enum 'ExactStopModal'>: <ExactStopModal.OFF: 64>, <enum 'Rotation'>: False}, 'H': 0, 'I': 0.0, 'J': 0.0, 'K': 0.0, 'L': 0.0, 'M': None, 'N': None, 'O': 0, 'P': 0.0, 'Q': 0.0, 'R': 0.0, 'S': 1, 'T': 0, 'U': 0.0001, 'V': 0.0001, 'W': 0.0001, 'X': 0.0, 'Y': 0.0, 'Z': 0.0, 'program': 'ncv', 'version': '0.0.0', 'packages_dir': 'projects/py38'}
    Moving the tool...
    Motion mode: MotionMode.RAPID
    Motion mode: MotionMode.RAPID