From 46eeb3a6155db4d3f844310cbe110ccb706563e4 Mon Sep 17 00:00:00 2001 From: P3ngSaM <61768364+P3ngSaM@users.noreply.github.com> Date: Thu, 17 Feb 2022 16:44:58 +0800 Subject: [PATCH] =?UTF-8?q?update=20ESG=E6=89=93=E5=88=86=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Esg/EsgModel.py | 69 ++++++- Esg/scripts/environmental.py | 101 ++++++----- Esg/scripts/governance.py | 57 +++--- Esg/scripts/social.py | 158 +++++++++------- Esg/static/template_data.json | 7 +- Report/PdfReport.py | 67 +++++-- Report/scripts/PdfStyle.py | 17 +- Report/static/ReportTemplates/ESG报告.json | 202 +++++++++++++++++++++ 8 files changed, 514 insertions(+), 164 deletions(-) create mode 100644 Report/static/ReportTemplates/ESG报告.json diff --git a/Esg/EsgModel.py b/Esg/EsgModel.py index 38ad95a..064f366 100644 --- a/Esg/EsgModel.py +++ b/Esg/EsgModel.py @@ -1,6 +1,9 @@ +import copy + from Esg.scripts.environmental import environmental_rating from Esg.scripts.governance import governance_rating from Esg.scripts.social import social_rating +from common.scripts import read_json_file class EsgModel: @@ -36,20 +39,70 @@ class EsgModel: self.cid = param['企业ID'] self.company = param['企业名称'] self.period = param['评价年度'] - self.questionnaire = param['填报问卷'] - self.esg_score = self.esg_rating_scripts() + self.esg_score = self.esg_rating_scripts(param) - def esg_rating_scripts(self): + @staticmethod + def esg_rating_scripts(param): """ esg打分方法 Parameters: - - + param dict 数据模板 Returns: score dict 分数 """ score = dict() - score['环境'] = environmental_rating(self.questionnaire) - score['社会责任'] = social_rating(self.questionnaire) - score['公司治理'] = governance_rating(self.questionnaire) - score['合计'] = sum(score.values()) + score['环境得分'] = environmental_rating(param) + score['社会得分'] = social_rating(param) + score['公司治理得分'] = governance_rating(param) + score['合计'] = score['环境得分']['合计'] + score['社会得分']['合计'] + score['公司治理得分']['合计'] return score + + def esg_rating_result(self): + """ + 整合打分结果 + Parameters: + score dict 打分结果 + Returns: + result dict esg得分 + """ + score = copy.deepcopy(self.esg_score) + result = dict() + result['企业名称'] = self.company + result['企业ID'] = self.cid + result['评价ID'] = self.rid + result['评价年度'] = self.period + result['评价等级'] = self.score_rank(score['合计']) + result['环境得分'] = score['环境得分'] + result['环境得分'] = score['环境得分'] + result['社会得分'] = score['环境得分'] + result['公司治理得分'] = score['环境得分'] + result['合计'] = score['合计'] + + return result + + @staticmethod + def score_rank(param): + """ + 描述 + Parameters: + param float esg分数合计 + Returns: + result str 评价等级 + """ + if param >= 80: + level = 'A' + elif param >= 50: + level = 'B' + elif param >= 30: + level = 'C' + else: + level = 'D' + + return level + + +if __name__ == '__main__': + m = EsgModel() + data = read_json_file('/Esg/static/template_data.json') + m.prepare_params(data) + r = m.esg_rating_result() diff --git a/Esg/scripts/environmental.py b/Esg/scripts/environmental.py index ece0b56..fe1d3a7 100644 --- a/Esg/scripts/environmental.py +++ b/Esg/scripts/environmental.py @@ -1,4 +1,4 @@ -from common.scripts import read_json_file +import operator def environmental_rating(param): @@ -9,25 +9,36 @@ def environmental_rating(param): Returns: score float 环境部分得分 """ + result = dict() + result['单位收入二氧化碳排放'] = calculation_01(param) + result['单位收入的能耗'] = calculation_02(param) + result['单位收入的耗水'] = calculation_03(param) + result['绿色业务收入占比(%)'] = calculation_04(param) + result['公司是否有温室气体减排目标'] = calculation_05(param) + result['企业是否有节能目标'] = calculation_06(param) + result['企业是否有节约用水目标'] = calculation_07(param) + result['是否有绿色业务'] = calculation_08(param) + result['近三年是否被环境或水务等监管部门处罚'] = calculation_09(param) + result['国家双碳目标对企业业务是否有不利影响'] = calculation_10(param) + result['国家双碳目标对企业业务是否有有利影响'] = calculation_11(param) + result['企业是否使用风电、光电、水电等清洁能源,是否使用清洁交通工具'] = calculation_02(param) + fraction = list() - if calculation_09(param) == 'E=0': + if result['近三年是否被环境或水务等监管部门处罚'] == 'E=0': score = 0 else: - fraction.append(calculation_01(param)) - fraction.append(calculation_02(param)) - fraction.append(calculation_03(param)) - fraction.append(calculation_04(param)) - fraction.append(calculation_05(param)) - fraction.append(calculation_06(param)) - fraction.append(calculation_07(param)) - fraction.append(calculation_08(param)) - fraction.append(calculation_10(param)) - fraction.append(calculation_11(param)) - fraction.append(calculation_12(param)) + for key, value in result.items(): + if key == '近三年是否被环境或水务等监管部门处罚': + continue + else: + fraction.append(value) + score = sum(fraction) - return score + result['合计'] = score + + return result def calculation_01(param): @@ -38,6 +49,8 @@ def calculation_01(param): Returns: score int 得分 """ + # Parameter + three_year_data = sorted(param['环境问卷']['近三年公司数据'], key=operator.itemgetter('年份'), reverse=False) def calculation_co2(data): """ @@ -62,15 +75,15 @@ def calculation_01(param): amount = sum(total) return amount - year_2020 = calculation_co2(param['环境问卷']['近三年公司数据'][0]) - year_2019 = calculation_co2(param['环境问卷']['近三年公司数据'][1]) - year_2018 = calculation_co2(param['环境问卷']['近三年公司数据'][2]) + year_begin = calculation_co2(three_year_data[0]) + year_middle = calculation_co2(three_year_data[1]) + year_end = calculation_co2(three_year_data[2]) - factor_01 = ((year_2020/year_2018)**(1/2)-1) <= -0.04 and year_2018 >= year_2019 >= year_2020 - factor_02 = -0.04 < ((year_2020/year_2018)**(1/2)-1) <= -0.03 and year_2018 >= year_2019 >= year_2020 - factor_03 = -0.03 < ((year_2020/year_2018)**(1/2)-1) <= -0.02 and year_2018 >= year_2019 >= year_2020 - factor_04 = -0.02 < ((year_2020/year_2018)**(1/2)-1) <= 0.01 and year_2018 >= year_2019 >= year_2020 - factor_05 = -0.01 < ((year_2020/year_2018)**(1/2)-1) <= 0 and year_2018 >= year_2019 >= year_2020 + factor_01 = ((year_end/year_begin)**(1/2)-1) <= -0.04 and year_begin >= year_middle >= year_end + factor_02 = -0.04 < ((year_end/year_begin)**(1/2)-1) <= -0.03 and year_begin >= year_middle >= year_end + factor_03 = -0.03 < ((year_end/year_begin)**(1/2)-1) <= -0.02 and year_begin >= year_middle >= year_end + factor_04 = -0.02 < ((year_end/year_begin)**(1/2)-1) <= 0.01 and year_begin >= year_middle >= year_end + factor_05 = -0.01 < ((year_end/year_begin)**(1/2)-1) <= 0 and year_begin >= year_middle >= year_end if factor_01: score = 15 @@ -96,6 +109,9 @@ def calculation_02(param): Returns: score int 得分 """ + # Parameter + three_year_data = sorted(param['环境问卷']['近三年公司数据'], key=operator.itemgetter('年份'), reverse=False) + def calculation_co2(data): """ 计算每年能耗 @@ -119,14 +135,14 @@ def calculation_02(param): amount = sum(total) return amount - year_2020 = calculation_co2(param['环境问卷']['近三年公司数据'][0]) - year_2019 = calculation_co2(param['环境问卷']['近三年公司数据'][1]) - year_2018 = calculation_co2(param['环境问卷']['近三年公司数据'][2]) + year_begin = calculation_co2(three_year_data[0]) + year_middle = calculation_co2(three_year_data[1]) + year_end = calculation_co2(three_year_data[2]) - factor_01 = ((year_2020/year_2018)**(1/2)-1) <= -0.028 and year_2018 >= year_2019 >= year_2020 - factor_02 = -0.028 < ((year_2020/year_2018)**(1/2)-1) <= -0.02 and year_2018 >= year_2019 >= year_2020 - factor_03 = -0.02 < ((year_2020/year_2018)**(1/2)-1) <= -0.01 and year_2018 >= year_2019 >= year_2020 - factor_04 = -0.01 < ((year_2020/year_2018)**(1/2)-1) <= 0 and year_2018 >= year_2019 >= year_2020 + factor_01 = ((year_end/year_begin)**(1/2)-1) <= -0.028 and year_begin >= year_middle >= year_end + factor_02 = -0.028 < ((year_end/year_begin)**(1/2)-1) <= -0.02 and year_begin >= year_middle >= year_end + factor_03 = -0.02 < ((year_end/year_begin)**(1/2)-1) <= -0.01 and year_begin >= year_middle >= year_end + factor_04 = -0.01 < ((year_end/year_begin)**(1/2)-1) <= 0 and year_begin >= year_middle >= year_end if factor_01: score = 11 @@ -150,12 +166,15 @@ def calculation_03(param): Returns: score int 得分 """ - year_2020 = param['环境问卷']['近三年公司数据'][0]['水(吨)'] - year_2019 = param['环境问卷']['近三年公司数据'][1]['水(吨)'] - year_2018 = param['环境问卷']['近三年公司数据'][2]['水(吨)'] + # Parameter + three_year_data = sorted(param['环境问卷']['近三年公司数据'], key=operator.itemgetter('年份'), reverse=False) - factor_01 = ((year_2020/year_2018)**(1/2)-1) <= -0.01 and year_2018 >= year_2019 >= year_2020 - factor_02 = -0.01 < ((year_2020/year_2018)**(1/2)-1) <= 0 and year_2018 >= year_2019 >= year_2020 + year_begin = three_year_data[0]['水(吨)'] + year_middle = three_year_data[1]['水(吨)'] + year_end = three_year_data[2]['水(吨)'] + + factor_01 = ((year_end/year_begin)**(1/2)-1) <= -0.01 and year_begin >= year_middle >= year_end + factor_02 = -0.01 < ((year_end/year_begin)**(1/2)-1) <= 0 and year_begin >= year_middle >= year_end if factor_01: score = 6 @@ -175,8 +194,11 @@ def calculation_04(param): Returns: score int 得分 """ - income = param['公司收入'][0] - green_income = param['环境问卷']['近三年公司数据'][0]['绿色业务收入(万元)'] + # Parameter + three_year_data = sorted(param['环境问卷']['近三年公司数据'], key=operator.itemgetter('年份'), reverse=False) + + income = param['公司收入'][three_year_data[2]['年份']] + green_income = three_year_data[2]['绿色业务收入(万元)'] prop = round(green_income / income, 2) if prop >= 50: @@ -201,6 +223,7 @@ def calculation_05(param): Returns: score int 得分 """ + # Parameter answer = param['环境问卷']['其他类型问卷'][0] if answer[0] == '是' and answer[1] is not None: @@ -335,9 +358,3 @@ def calculation_12(param): score = 0 return score - - -if __name__ == '__main__': - data = read_json_file('/Esg/static/template_data.json') - E_score = environmental_rating(data) - print(E_score) diff --git a/Esg/scripts/governance.py b/Esg/scripts/governance.py index bb05328..73b4f0a 100644 --- a/Esg/scripts/governance.py +++ b/Esg/scripts/governance.py @@ -1,4 +1,4 @@ -from common.scripts import read_json_file +import operator def governance_rating(param): @@ -9,20 +9,25 @@ def governance_rating(param): Returns: score float 治理部分得分 """ + result = dict() + result['企业性质'] = calculation_01(param) + result['公司是否设有董事会'] = calculation_02(param) + result['公司是否设有监事会'] = calculation_03(param) + result['董监高平均拥有的行业经验年数'] = calculation_04(param) + result['董监高近三年离职率'] = calculation_05(param) + result['公司近三年信息披露及时、可靠、完备、审计质量'] = calculation_06(param) + result['公司董事会近三年年均开会次数'] = calculation_07(param) + result['净资产收益率'] = calculation_08(param) + result['公司是否有审计报告'] = calculation_09(param) + fraction = list() + for value in result.values(): + fraction.append(value) - fraction.append(calculation_01(param)) - fraction.append(calculation_02(param)) - fraction.append(calculation_03(param)) - fraction.append(calculation_04(param)) - fraction.append(calculation_05(param)) - fraction.append(calculation_06(param)) - fraction.append(calculation_07(param)) - fraction.append(calculation_08(param)) - fraction.append(calculation_09(param)) score = sum(fraction) + result['合计'] = score - return score + return result def calculation_01(param): @@ -165,12 +170,14 @@ def calculation_08(param): Returns: score int 得分 """ - data = param['治理问卷']['近三年公司数据'] - year_2020 = data[0]['公司净利润(万元)'] / data[0]['公司净资产(万元)'] * 100 - year_2019 = data[1]['公司净利润(万元)'] / data[1]['公司净资产(万元)'] * 100 - year_2018 = data[2]['公司净利润(万元)'] / data[2]['公司净资产(万元)'] * 100 + # Parameter + three_year_data = sorted(param['治理问卷']['近三年公司数据'], key=operator.itemgetter('年份'), reverse=False) - average = (year_2020 + year_2019 + year_2018)/3 + year_begin = three_year_data[0]['公司净利润(万元)'] / three_year_data[0]['公司净资产(万元)'] * 100 + year_middle = three_year_data[1]['公司净利润(万元)'] / three_year_data[1]['公司净资产(万元)'] * 100 + year_end = three_year_data[2]['公司净利润(万元)'] / three_year_data[2]['公司净资产(万元)'] * 100 + + average = (year_end + year_middle + year_begin)/3 if average >= 15: score = 6 @@ -192,20 +199,16 @@ def calculation_09(param): Returns: score int 得分 """ - data = param['治理问卷']['近三年公司数据'] - year_2020 = data[0]['公司是否有审计报告'] - year_2019 = data[1]['公司是否有审计报告'] - year_2018 = data[2]['公司是否有审计报告'] + # Parameter + three_year_data = sorted(param['治理问卷']['近三年公司数据'], key=operator.itemgetter('年份'), reverse=False) - if year_2020 == year_2019 == year_2018 == '是': + year_begin = three_year_data[0]['公司是否有审计报告'] + year_middle = three_year_data[1]['公司是否有审计报告'] + year_end = three_year_data[2]['公司是否有审计报告'] + + if year_end == year_middle == year_begin == '是': score = 2 else: score = 0 return score - - -if __name__ == '__main__': - data = read_json_file('/Esg/static/template_data.json') - S_score = governance_rating(data) - print(S_score) \ No newline at end of file diff --git a/Esg/scripts/social.py b/Esg/scripts/social.py index a3d8666..41ab1e0 100644 --- a/Esg/scripts/social.py +++ b/Esg/scripts/social.py @@ -1,4 +1,4 @@ -from common.scripts import read_json_file +import operator def social_rating(param): @@ -9,28 +9,39 @@ def social_rating(param): Returns: score float 社会部分得分 """ + result = dict() + result['离职人数占比'] = calculation_01(param) + result['人均薪酬涨幅'] = calculation_02(param) + result['劳动合同中的工作时长(周)'] = calculation_03(param) + result['劳动纠纷'] = calculation_04(param) + result['安全事故'] = calculation_05(param) + result['提供培训'] = calculation_06(param) + result['社保缴纳是否符合当地标准'] = calculation_07(param) + result['公积金缴纳是否符合当地标准'] = calculation_08(param) + result['是否提供员工体检'] = calculation_09(param) + result['是否提供带薪假期'] = calculation_10(param) + result['公司从前三大供货商拿货占比'] = calculation_11(param) + result['公司前3大客户销量占比'] = calculation_12(param) + result['公司返修、退回、投诉产品比例(%)'] = calculation_13(param) + result['扶贫+捐赠规模(万元)'] = calculation_14(param) + result['司法风险'] = calculation_15(param) + fraction = list() - if calculation_15(param) == 'S=0': + if result['司法风险'] == 'S=0': score = 0 else: - fraction.append(calculation_01(param)) - fraction.append(calculation_02(param)) - fraction.append(calculation_03(param)) - fraction.append(calculation_04(param)) - fraction.append(calculation_05(param)) - fraction.append(calculation_06(param)) - fraction.append(calculation_07(param)) - fraction.append(calculation_08(param)) - fraction.append(calculation_09(param)) - fraction.append(calculation_10(param)) - fraction.append(calculation_11(param)) - fraction.append(calculation_12(param)) - fraction.append(calculation_13(param)) - fraction.append(calculation_14(param)) + for key, value in result.items(): + if key == '司法风险': + continue + else: + fraction.append(value) + score = sum(fraction) - return score + result['合计'] = score + + return result def calculation_01(param): @@ -41,13 +52,15 @@ def calculation_01(param): Returns: score int 得分 """ - social_data = param['社会问卷']['近三年公司数据'] - year_2020 = round(social_data[0]['当年离职人数'] / social_data[0]['员工总数'], 2) - year_2019 = round(social_data[1]['当年离职人数'] / social_data[1]['员工总数'], 2) - year_2018 = round(social_data[2]['当年离职人数'] / social_data[2]['员工总数'], 2) + # Parameter + three_year_data = sorted(param['社会问卷']['近三年公司数据'], key=operator.itemgetter('年份'), reverse=False) - factor_01 = (year_2018 + year_2019 + year_2020)/3*100 <= 20 - factor_02 = 20 < (year_2018 + year_2019 + year_2020)/3*100 <= 30 + year_begin = round(three_year_data[0]['当年离职人数'] / three_year_data[0]['员工总数'], 2) + year_middle = round(three_year_data[1]['当年离职人数'] / three_year_data[1]['员工总数'], 2) + year_end = round(three_year_data[2]['当年离职人数'] / three_year_data[2]['员工总数'], 2) + + factor_01 = (year_begin + year_middle + year_end) / 3 * 100 <= 20 + factor_02 = 20 < (year_begin + year_middle + year_end) / 3 * 100 <= 30 if factor_01: score = 5 @@ -67,10 +80,13 @@ def calculation_02(param): Returns: score int 得分 """ - year_2020 = param['社会问卷']['近三年公司数据'][0]['人均薪酬水平(元/月)'] - year_2018 = param['社会问卷']['近三年公司数据'][2]['人均薪酬水平(元/月)'] + # Parameter + three_year_data = sorted(param['社会问卷']['近三年公司数据'], key=operator.itemgetter('年份'), reverse=False) - factor = (year_2020/year_2018)**(1/2) - 1 >= 0.03 + year_begin = three_year_data[0]['人均薪酬水平(元/月)'] + year_end = three_year_data[2]['人均薪酬水平(元/月)'] + + factor = (year_end / year_begin) ** (1 / 2) - 1 >= 0.03 if factor: score = 2 @@ -88,11 +104,14 @@ def calculation_03(param): Returns: score int 得分 """ - year_2020 = param['社会问卷']['近三年公司数据'][0]['劳动合同要求工作时长(每周小时数)'] - year_2019 = param['社会问卷']['近三年公司数据'][1]['劳动合同要求工作时长(每周小时数)'] - year_2018 = param['社会问卷']['近三年公司数据'][2]['劳动合同要求工作时长(每周小时数)'] + # Parameter + three_year_data = sorted(param['社会问卷']['近三年公司数据'], key=operator.itemgetter('年份'), reverse=False) - factor = (year_2020 + year_2019 + year_2018) / 3 <= 45 + year_begin = three_year_data[0]['劳动合同要求工作时长(每周小时数)'] + year_middle = three_year_data[1]['劳动合同要求工作时长(每周小时数)'] + year_end = three_year_data[2]['劳动合同要求工作时长(每周小时数)'] + + factor = (year_end + year_middle + year_begin) / 3 <= 45 if factor: score = 4 @@ -110,11 +129,14 @@ def calculation_04(param): Returns: score int 得分 """ - year_2020 = param['社会问卷']['近三年公司数据'][0]['劳资纠纷次数'] - year_2019 = param['社会问卷']['近三年公司数据'][1]['劳资纠纷次数'] - year_2018 = param['社会问卷']['近三年公司数据'][2]['劳资纠纷次数'] + # Parameter + three_year_data = sorted(param['社会问卷']['近三年公司数据'], key=operator.itemgetter('年份'), reverse=False) - factor = (year_2020 + year_2019 + year_2018) <= 0 + year_begin = three_year_data[0]['劳资纠纷次数'] + year_middle = three_year_data[1]['劳资纠纷次数'] + year_end = three_year_data[2]['劳资纠纷次数'] + + factor = (year_end + year_middle + year_begin) <= 0 if factor: score = 3 @@ -132,11 +154,14 @@ def calculation_05(param): Returns: score int 得分 """ - year_2020 = param['社会问卷']['近三年公司数据'][0]['安全事故发生次数'] - year_2019 = param['社会问卷']['近三年公司数据'][1]['安全事故发生次数'] - year_2018 = param['社会问卷']['近三年公司数据'][2]['安全事故发生次数'] + # Parameter + three_year_data = sorted(param['社会问卷']['近三年公司数据'], key=operator.itemgetter('年份'), reverse=False) - factor = (year_2020 + year_2019 + year_2018) <= 0 + year_begin = three_year_data[0]['安全事故发生次数'] + year_middle = three_year_data[1]['安全事故发生次数'] + year_end = three_year_data[2]['安全事故发生次数'] + + factor = (year_end + year_middle + year_begin) <= 0 if factor: score = 3 @@ -154,12 +179,15 @@ def calculation_06(param): Returns: score int 得分 """ - year_2020 = param['社会问卷']['近三年公司数据'][0]['组织培训次数'] - year_2019 = param['社会问卷']['近三年公司数据'][1]['组织培训次数'] - year_2018 = param['社会问卷']['近三年公司数据'][2]['组织培训次数'] + # Parameter + three_year_data = sorted(param['社会问卷']['近三年公司数据'], key=operator.itemgetter('年份'), reverse=False) - factor_01 = (year_2020 + year_2019 + year_2018) >= 4 - factor_02 = (year_2020 + year_2019 + year_2018) >= 1 + year_begin = three_year_data[0]['组织培训次数'] + year_middle = three_year_data[1]['组织培训次数'] + year_end = three_year_data[2]['组织培训次数'] + + factor_01 = (year_end + year_middle + year_begin) >= 4 + factor_02 = (year_end + year_middle + year_begin) >= 1 if factor_01: score = 5 @@ -251,10 +279,13 @@ def calculation_11(param): Returns: score int 得分 """ - year_2020 = param['社会问卷']['近三年公司数据'][0]['公司从前3大供应商拿货占全部供应商比例(%)'] - year_2018 = param['社会问卷']['近三年公司数据'][2]['公司从前3大供应商拿货占全部供应商比例(%)'] + # Parameter + three_year_data = sorted(param['社会问卷']['近三年公司数据'], key=operator.itemgetter('年份'), reverse=False) - factor = year_2018 >= year_2020 + year_begin = three_year_data[0]['公司从前3大供应商拿货占全部供应商比例(%)'] + year_end = three_year_data[2]['公司从前3大供应商拿货占全部供应商比例(%)'] + + factor = year_begin >= year_end if factor: score = 1 @@ -272,10 +303,13 @@ def calculation_12(param): Returns: score int 得分 """ - year_2020 = param['社会问卷']['近三年公司数据'][0]['公司前3大客户销售额占全部销售比例(%)'] - year_2018 = param['社会问卷']['近三年公司数据'][2]['公司前3大客户销售额占全部销售比例(%)'] + # Parameter + three_year_data = sorted(param['社会问卷']['近三年公司数据'], key=operator.itemgetter('年份'), reverse=False) - factor = year_2018 >= year_2020 + year_begin = three_year_data[0]['公司前3大客户销售额占全部销售比例(%)'] + year_end = three_year_data[2]['公司前3大客户销售额占全部销售比例(%)'] + + factor = year_begin >= year_end if factor: score = 1 @@ -293,10 +327,13 @@ def calculation_13(param): Returns: score int 得分 """ - year_2020 = param['社会问卷']['近三年公司数据'][0]['返修、退回、投诉产品对应销售额占全部销售比例(%)'] - year_2018 = param['社会问卷']['近三年公司数据'][2]['返修、退回、投诉产品对应销售额占全部销售比例(%)'] + # Parameter + three_year_data = sorted(param['社会问卷']['近三年公司数据'], key=operator.itemgetter('年份'), reverse=False) - factor = year_2018 >= year_2020 + year_end = three_year_data[0]['返修、退回、投诉产品对应销售额占全部销售比例(%)'] + year_begin = three_year_data[2]['返修、退回、投诉产品对应销售额占全部销售比例(%)'] + + factor = year_begin >= year_end if factor: score = 1 @@ -314,11 +351,14 @@ def calculation_14(param): Returns: score int 得分 """ - year_2020 = param['社会问卷']['近三年公司数据'][0]['扶贫+捐赠规模(万元)'] - year_2019 = param['社会问卷']['近三年公司数据'][1]['扶贫+捐赠规模(万元)'] - year_2018 = param['社会问卷']['近三年公司数据'][2]['扶贫+捐赠规模(万元)'] + # Parameter + three_year_data = sorted(param['社会问卷']['近三年公司数据'], key=operator.itemgetter('年份'), reverse=False) - factor_01 = (year_2020 + year_2019 + year_2018) > 0 + year_begin = three_year_data[0]['扶贫+捐赠规模(万元)'] + year_middle = three_year_data[1]['扶贫+捐赠规模(万元)'] + year_end = three_year_data[2]['扶贫+捐赠规模(万元)'] + + factor_01 = (year_end + year_middle + year_begin) > 0 if factor_01: score = 5 @@ -346,9 +386,3 @@ def calculation_15(param): score = 0 return score - - -if __name__ == '__main__': - data = read_json_file('/Esg/static/template_data.json') - S_score = social_rating(data) - print(S_score) \ No newline at end of file diff --git a/Esg/static/template_data.json b/Esg/static/template_data.json index b62c415..7d465e7 100644 --- a/Esg/static/template_data.json +++ b/Esg/static/template_data.json @@ -2,8 +2,13 @@ "评价ID": "EsfsSg02", "企业ID": "Z1tfsw6r", "企业名称": "远东资信评估有限公司", + "评价年度": "2021年", "所属行业": ["制造业", "化学工业"], - "公司收入": [200232, 253014, 190123], + "公司收入": { + "2018年": 203156, + "2019年": 203156, + "2020年": 203156 + }, "环境问卷": { "近三年公司数据": [ { diff --git a/Report/PdfReport.py b/Report/PdfReport.py index 5cbd524..694eabd 100644 --- a/Report/PdfReport.py +++ b/Report/PdfReport.py @@ -11,17 +11,18 @@ from Report.scripts.PdfStyle import cover_space, cover_company_style, cover_repo chapter_style, \ section_style, adjust_line_width, para_style_single, para_style_bold, table_name, table_unit, table_mark, \ adjust_table_widths, adjust_table_style, Seashell4, toc_style_1, table_content_style, toc_style_2, darkGolden, \ - adjust_table_data + adjust_table_data, para_bold_style, para_style_esg from Report.scripts.path_tool import gen_pdf_path, get_pic_path - +from common.scripts import read_json_file # 页码起始设置 -offset = 3 +# offset = 3 class HeaderAndFooterCanvas(canvas.Canvas): def __init__(self, *args, **kwargs): canvas.Canvas.__init__(self, *args, **kwargs) self._saved_page_states = [] + self.offeset = 2 def showPage(self): self._saved_page_states.append(dict(self.__dict__)) @@ -29,16 +30,12 @@ class HeaderAndFooterCanvas(canvas.Canvas): def draw_header_and_footer(self, page_count): - # 把封面加上 - # if self._pageNumber == 1: - # self.drawImage(get_pic_path(picName='cover.png'), 0, 0, 596, 842) - self.setFont("Helvetica", 7) - if self._pageNumber > offset: + if self._pageNumber > self.offeset: self.setFont('pingbold', 8) self.setFillColor(HexColor(Seashell4)) self.drawString(74, 805, "远东资信评估有限公司") - self.drawString(74, 40, "%d / %d" % (self._pageNumber - offset, page_count - offset)) + self.drawString(74, 40, "%d / %d" % (self._pageNumber - self.offeset, page_count - self.offeset)) self.setStrokeColor(darkGolden) self.setLineWidth(2.5) @@ -57,10 +54,11 @@ class HeaderAndFooterCanvas(canvas.Canvas): class MyDocTemplate(BaseDocTemplate): def __init__(self, filename, **kw): - self.allowSplitting = 0 + self.allowSplitting = 1 BaseDocTemplate.__init__(self, filename, **kw) template = PageTemplate('normal', [Frame(2.5 * cm, 2.5 * cm, 15 * cm, 25 * cm, id='F1')]) self.addPageTemplates(template) + self.offeset = kw['offeset'] def afterFlowable(self, flowable): if flowable.__class__.__name__ == 'Paragraph': @@ -74,7 +72,7 @@ class MyDocTemplate(BaseDocTemplate): else: return - e = (level, text, self.page - offset) + e = (level, text, self.page - self.offeset) self.notify('TOCEntry', e) @@ -83,19 +81,26 @@ class ReportGenerator: def __init__(self, **kwargs): # 文本数据 self.text_model = kwargs['text_model'] - # 调用模板,在临时报告文件夹中创建指定名称的PDF文档 - self.doc = MyDocTemplate(gen_pdf_path(name=kwargs['name'])) + if '报告类型' in self.text_model: + # 调用模板,在临时报告文件夹中创建指定名称的PDF文档 + self.doc = MyDocTemplate(gen_pdf_path(name=kwargs['name']), offeset=2) + else: + self.doc = MyDocTemplate(gen_pdf_path(name=kwargs['name']), offeset=3) # 内容框 self.story = list() # 生成报告 def gen_report(self): - self.gen_cover() - self.gen_menu() - self.gen_main_part() - - # 将内容输出到PDF中 - self.doc.multiBuild(self.story, canvasmaker=HeaderAndFooterCanvas) + if '报告类型' in self.text_model: + self.gen_esg_cover() + self.gen_menu() + self.gen_esg_part() + self.doc.multiBuild(self.story, canvasmaker=HeaderAndFooterCanvas) + else: + self.gen_cover() + self.gen_menu() + self.gen_main_part() + self.doc.multiBuild(self.story, canvasmaker=HeaderAndFooterCanvas) # 设置章节、小节标题 def set_head(self, text, sty): @@ -123,6 +128,15 @@ class ReportGenerator: self.story.append(Paragraph(data['生成日期'], cover_time_style)) self.story.append(PageBreak()) + def gen_esg_cover(self): + data = self.text_model + self.story.append(Paragraph('.', cover_space)) + self.story.append(Paragraph(data['企业名称'], cover_company_style)) + self.story.append(Paragraph('ESG评价报告', cover_report_style)) + self.story.append(Image(get_pic_path(pic_name='ydzx.png'), width=202, height=37)) + self.story.append(Paragraph(data['生成日期'], cover_time_style)) + self.story.append(PageBreak()) + # 正文 def gen_main_part(self): data = self.text_model['报告内容'] @@ -161,3 +175,18 @@ class ReportGenerator: style = adjust_table_style(part['表格']) td = adjust_table_data(part['表格']) self.story.append(Table(td, style=style)) + + def gen_esg_part(self): + data = self.text_model['报告内容'] + for chapter in data: + self.set_head(chapter['章节'], chapter_style) + for section in chapter['章节内容']: + # 段落 + if list(section.keys())[0] == '段落': + self.story.append(Paragraph(adjust_line_width(section['段落'], 8), para_style_single)) + + +if __name__ == '__main__': + text_data = read_json_file('/Report/static/ReportTemplates/ESG报告.json') + m = ReportGenerator(name=text_data['企业名称'], text_model=text_data) + m.gen_report() diff --git a/Report/scripts/PdfStyle.py b/Report/scripts/PdfStyle.py index e3132ea..b3d07df 100644 --- a/Report/scripts/PdfStyle.py +++ b/Report/scripts/PdfStyle.py @@ -52,16 +52,23 @@ table_name = PS(name="table_name", fontName="pingbold", fontSize=8, leading=16, table_unit = PS(name="table_unit", fontName="SimHei", fontSize=6, leading=8, alignment=TA_RIGHT, spaceBefore=2) table_mark = PS(name="table_mark", fontName="SimHei", fontSize=6, leading=16, alignment=TA_LEFT, spaceBefore=2) table_style = getSampleStyleSheet() -table_style.add(ParagraphStyle(fontName='SimHei', name='Song', leading=12, fontSize=8, spaceBefore=2, alignment=TA_CENTER)) -table_style.add(ParagraphStyle(fontName='SimHei', name='Long', leading=12, fontSize=8, spaceBefore=2, alignment=TA_LEFT)) -table_style.add(ParagraphStyle(fontName='SimHei', name='Longs', leading=9, fontSize=6, spaceBefore=2, alignment=TA_LEFT)) -table_style.add(ParagraphStyle(fontName='SimHei', name='Songs', leading=9, fontSize=6, spaceBefore=2, alignment=TA_CENTER)) -table_style.add(ParagraphStyle(fontName='SimHei', name='Song_small', leading=7, fontSize=5, spaceBefore=2, alignment=TA_CENTER)) +table_style.add( + ParagraphStyle(fontName='SimHei', name='Song', leading=12, fontSize=8, spaceBefore=2, alignment=TA_CENTER)) +table_style.add( + ParagraphStyle(fontName='SimHei', name='Long', leading=12, fontSize=8, spaceBefore=2, alignment=TA_LEFT)) +table_style.add( + ParagraphStyle(fontName='SimHei', name='Longs', leading=9, fontSize=6, spaceBefore=2, alignment=TA_LEFT)) +table_style.add( + ParagraphStyle(fontName='SimHei', name='Songs', leading=9, fontSize=6, spaceBefore=2, alignment=TA_CENTER)) +table_style.add( + ParagraphStyle(fontName='SimHei', name='Song_small', leading=7, fontSize=5, spaceBefore=2, alignment=TA_CENTER)) # para para_style_single = PS(name="para_style_single", fontName="SimHei", fontSize=8, leading=18, alignment=TA_LEFT, spaceBefore=6) para_style_normal = PS(name="para_style_normal", fontName="SimHei", fontSize=8, leading=18, alignment=TA_LEFT, spaceBefore=6, firstLineIndent=16) +para_style_esg = PS(name="para_style_esg", fontName="SimHei", fontSize=10, leading=18, alignment=TA_LEFT, + spaceBefore=6) para_style_bold = PS(name="para_style_bold", fontName="pingbold", fontSize=10, leading=18, alignment=TA_LEFT, spaceBefore=6) para_bold_style = PS(name="para_style", fontName="pingbold", fontSize=9, leading=18, alignment=TA_LEFT, spaceBefore=6) diff --git a/Report/static/ReportTemplates/ESG报告.json b/Report/static/ReportTemplates/ESG报告.json new file mode 100644 index 0000000..bfb3c44 --- /dev/null +++ b/Report/static/ReportTemplates/ESG报告.json @@ -0,0 +1,202 @@ +{ + "企业名称": "远东资信评估有限公司", + "生成日期": "2022-02-14", + "报告类型": "ESG", + "报告内容": [ + { + "章节": "声明", + "章节内容": [ + { + "段落": "本ESG评价报告主要基于公司提供信息,依据合理的标准和程序做出的独立分析和判断。远东资信评估有限公司不对因使用本报告意见而产生的后果承担责任。本报告可能存在固有局限性,例如,信息主要由公司提供,而远东资信无法对信息正确性进行分辨。" + }, + { + "段落": "为减少企业填写、搜集信息工作量,也为了标准化之目的,我们对模型做了简化处理。如果企业有意愿进一步了解ESG,我们很愿意提供有针对性、线下、定制化的咨询服务。" + }, + { + "段落": "评价不是目的,为企业提供帮助是我们的初衷。" + } + ] + }, + { + "章节": "远东资信", + "章节内容": [ + { + "段落": "远东资信评估有限公司(以下简称“远东资信”)成立于1988年,是中国第一家社会化专业资信评估公司,是信用评级领域的头部公司。在绿色低碳方面,远东资信已在绿色金融、碳金融、ESG领域积累了丰富经验。" + }, + { + "段落": "远东资信受天府股交中心委托,将为挂牌企业提供ESG服务。" + }, + { + "段落": "联系电话:{}" + }, + { + "段落": "联系人:{}" + } + ] + }, + { + "章节": "ESG", + "章节内容": [ + { + "段落": "ESG是“环境(Environmental)”、“社会(Social)”和“公司治理(Governance)”的简称,关注的是企业与环境的相互影响、履行社会责任的情况和公司治理绩效。ESG与企业业绩水平和长远发展存在密切关系,已受到全世界企业、投资者的广泛认可。" + }, + { + "段落": "在港上市公司已经在按照监管要求披露ESG报告,A股上市公司也开始发布社会责任报告、可持续发展报告、ESG报告。证监会正在制定相关信息披露制度,以使A股上市公司能按照要求披露标准化的ESG信息。" + } + ] + }, + { + "章节": "模型说明", + "章节内容": [ + { + "段落": "我们的ESG模型,参考了国际主流ESG体系架构,结合中国资本市场发展情况、监管政策和公司ESG实践,形成了本土化特色指标体系,此外,这次是受股交中心委托对挂牌中小企业做ESG评价,因此,指标考虑了中小企业特征。我们将指标细分为基础指标和调节指标,对于基础指标,我们按照重要性赋予分值,合计100分,调节指标是在公司基础分数之上给予加减分处理,比如公司被监管处罚过,或者有绿色低碳业务等。" + }, + { + "段落": "在此基础上,我们对公司划分等级,如下" + }, + { + "段落": "A——对应中小企业ESG为优秀级别" + }, + { + "段落": "B——对应中小企业ESG为正常级别" + }, + { + "段落": "C——对应中小企业ESG为关注级别" + }, + { + "段落": "D——对应中小企业ESG为不合格级别" + } + ] + }, + { + "章节": "基本信息", + "章节内容": [ + { + "段落": "公司名称:{}" + }, + { + "段落": "所属行业:{}" + } + ] + }, + { + "章节": "公司ESG等级", + "章节内容": [ + { + "段落": "经分析,公司ESG等级为{}" + } + ] + }, + { + "章节": "环境部分", + "章节内容": [ + { + "段落": "我们重点关注的环境因素包括二氧化碳排放、能耗、资源消耗、绿色业务等情况。" + }, + { + "段落": "二氧化碳——二氧化碳主要来自(1)企业直接消耗的燃料比如燃气、汽柴油等所导致的温室气体排放;(2)企业购买并使用电力等所产生的间接排放。" + }, + { + "段落": "二氧化碳排放——{date_range}年,公司总的二氧化碳排放(单位:千克){co2_emissions}。" + }, + { + "段落": "单位二氧化碳排放——{date_range}年,公司每万元收入二氧化碳排放(单位:千克){CO2_year_01}、{CO2_year_02}、{CO2_year_03}。{year_02}年、{year_03}年同比分别为{year01_on_year02}、{year02_on_year03}。{year_03}年,同行业公司该指标区间为{industry_ indicators},该指标均值为{industry_ indicators_average},该指标中位数为{industry_ indicators_median},公司较之行业中位数{median_comparison}。" + }, + { + "段落": "能耗——能耗包括企业消耗的电量、汽柴油等。为表述方便,我们将不同能源消耗均转为以兆焦耳做单位。" + }, + { + "段落": "能耗——{date_range}年,公司总的能耗(单位:兆焦耳){energy_consumption_year_01}、{energy_consumption_year_02}、{energy_consumption_year_03}。" + }, + { + "段落": "单位能耗——{date_range}年,公司每万元收入能耗(单位:兆焦耳){sec_year_01}、{sec_year_02}、{sec_year_03}。{year_02}年、{year_03}年同比增长分别为{}、{}。{year_03}年,同行业公司该指标区间为{industry_ indicators},该指标均值为{industry_ indicators_average},该指标中位数为{industry_ indicators_median},公司较之行业中位数{median_comparison}。" + }, + { + "段落": "能耗折算标准煤——{date_range}年,公司总的能耗折算为标准煤(单位:千克){standard_coal_year_01}、{standard_coal_year_02}、{standard_coal_year_03}。。" + }, + { + "段落": "单位能耗折算标准煤——{date_range}年,公司每万元收入能耗折算为标准煤(单位:千克){standard_coal_year_01}、{standard_coal_year_02}、{standard_coal_year_03}。。" + }, + { + "段落": "耗水——{date_range}年,公司总耗水(单位:吨){consume water_year_01}、{consume water_year_02}、{consume water_year_03}。" + }, + { + "段落": "单位耗水——{date_range}年,公司每万元收入耗水(单位:吨){iwc_year_01}、{iwc_year_01}、{iwc_year_01}。{year_02}年、{year_03}年同比增长分别为{year01_on_year02}、{year02_on_year03}。{year_03}年,同行业公司该指标区间为{industry_ indicators},该指标均值为{industry_ indicators_average},该指标中位数为{industry_ indicators_median},公司较之行业中位数{median_comparison}。" + }, + { + "段落": "{describe}" + }, + { + "段落": "{describe}" + } + ] + }, + { + "章节": "社会部分", + "章节内容": [ + { + "段落": "我们重点关注的社会因素包括雇佣、供应链、产品、社会贡献等。" + }, + { + "段落": "离职率——{date_range}年,公司离职比例为{turnover_ratio_year_01}、{turnover_ratio_year_02}、{turnover_ratio_year_03}。{year_03}年,同行业公司该指标区间为{industry_ indicators},该指标均值为{industry_ indicators_average},该指标中位数为{industry_ indicators_median},公司较之行业中位数{median_comparison}。" + }, + { + "段落": "人均薪酬水平——{date_range}年,公司人均薪酬水平为{salary_level_year_01}、{salary_level_year_02}、{salary_level_year_03}。{}年、{}年同比增长分别为{}、{}。{}年,同行业公司该指标区间为{},该指标均值为{},该指标中位数为{},公司较之行业中位数{}。" + }, + { + "段落": "劳动纠纷——公司在近三年出现了劳动纠纷,这影响了公司的社会得分。" + }, + { + "段落": "安全事故——公司在近三年出现了安全事故,这影响了公司的社会得分。" + }, + { + "段落": "提供培训——{date_range}年,公司培训次数为{}、{}、{}。{year_03}年,同行业公司该指标均值为{}。" + }, + { + "段落": "产品返修、退回、投诉——{date_range}年,公司返修、退回、投诉产品比例(%){rrc_year_01}、{rrc_year_02}、{rrc_year_03}。{year_03}年,同行业公司该指标均值为{industry_ indicators_average}。" + }, + { + "段落": "扶贫和捐赠——公司在近三年有过扶贫、捐赠行为,这提高了公司社会得分。" + }, + { + "段落": "法律诉讼(作为被告)、被执行人、失信人、行政处罚、税收违法、严重违法、经营异常——经查,公司存在1项或多项的法律诉讼(作为被告)、被执行人、失信人、行政处罚、税收违法、严重违法、经营异常情况,这影响了公司社会分数。" + } + ] + + }, + { + "章节": "公司治理部分", + "章节内容": [ + { + "段落": "我们重点关注的公司治理因素包括企业性质、董监高、专业能力、信息披露等。" + }, + { + "段落": "董事会——公司未设立董事会,这影响了公司在治理方面的得分。" + }, + { + "段落": "监事会——公司未设立监事会,这影响了公司在治理方面的得分。" + }, + { + "段落": "董监高专业性——公司董监高平均拥有的行业经验年数{experience_year},同行业公司该指标均值为{industry_ indicators_average}。" + }, + { + "段落": "董监高稳定性——公司董监高近3年离职率较高,这影响了公司在治理方面的得分。" + }, + { + "段落": "董事会——公司董事会近三年年均开会次数{},同行业公司该指标均值为{industry_ indicators_average}。" + } + ] + }, + { + "章节": "跋文", + "章节内容": [ + { + "段落": "我们不确定企业有足够的耐心填写调查问卷,因此我们提供的问卷模板是非常简化的,相应的模型也做了简化处理。我们不确定企业有足够的耐心阅读ESG报告,因此在内容上我们尽量做的简单明了。" + }, + { + "段落": "我们能确定的是ESG对企业发展非常有帮助,而我们在这方面有着深入的理解和丰富的经验,可以为企业提供帮助,欢迎有兴趣的企业咨询。" + } + ] + } + ] +} \ No newline at end of file