diff --git a/.gitignore b/.gitignore index b3103ac..67401ed 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ -output +static/plots data +build .svn \ No newline at end of file diff --git a/README.md b/README.md index aebb324..430828a 100644 --- a/README.md +++ b/README.md @@ -24,4 +24,6 @@ python owid.py python dress.py ``` -**NOTE** : `-r` option is used to fetch update data \ No newline at end of file +**NOTE** : +* `-r` option is used to fetch update data +* `dress.py` includes **--to-html** to generate html page with plots \ No newline at end of file diff --git a/drees.py b/drees.py index d4c45bd..ca57d72 100644 --- a/drees.py +++ b/drees.py @@ -10,6 +10,7 @@ from typing import Any, Dict, List, Optional, OrderedDict, Tuple import numpy as np import requests +from jinja2 import Environment, FileSystemLoader, select_autoescape from matplotlib import dates as md from matplotlib import pyplot as plt @@ -20,7 +21,11 @@ DATE_FORMAT = "%Y-%m-%d" DATA_URL = "https://data.drees.solidarites-sante.gouv.fr/api/records/1.0/search/?dataset=covid-19-resultats-par-age-issus-des-appariements-entre-si-vic-si-dep-et-vac-si&q=&rows=-1&facet=date&facet=vac_statut&facet=age" DATA_REPOSITORY = "data" -OUTPUT_REPOSITORY = "output" +STATIC_REPOSITORY = "static" +OUTPUT_REPOSITORY = os.path.join(STATIC_REPOSITORY, "plots") +BUILD_REPOSITORY = "build" + +MAIN_URL = "https://covid.thegux.fr/" # cycler could be better, but for ages plots it's ok AGE_COLORS = { @@ -41,9 +46,9 @@ class DreesEnum(bytes, Enum): class Field(DreesEnum): - HC = (0, "hc") - SC = (1, "sc") - DC = (2, "dc") + HC = (0, "Hospitalisations") + SC = (1, "Soins critiques") + DC = (2, "Décés") class VacStatus(DreesEnum): @@ -109,7 +114,7 @@ def get_enum_age(value): def get_enum_field(value): for field in Field: - if field.label == value: + if field.name.lower() == value: return field.value @@ -141,9 +146,8 @@ def group_by_age_date(data: Dict[str, Any]) -> Dict[dt, Any]: if vac_status not in dic_data_grouped[date][age]: dic_data_grouped[date][age][vac_status] = OrderedDict() for field in Field: - dic_data_grouped[date][age][vac_status][field.label] = row_fields[ - field.label - ] + field_name = field.name.lower() + dic_data_grouped[date][age][vac_status][field_name] = row_fields[field_name] logging.info("data restructured") return dic_data_grouped @@ -284,7 +288,7 @@ def analyse(np_data: np.ndarray, np_date: np.ndarray) -> None: mean_vac_percent = np.round( np.nanmean(np_percent_vac[:, age_group.value, field.value]) * 100, 2 ) - print(f"{field.label} - {age_group.label} - vac : {mean_vac_percent}%") + print(f"{field.name} - {age_group.label} - vac : {mean_vac_percent}%") logging.info("--- age by field and vac status mean percent ---") for field in Field: @@ -297,22 +301,20 @@ def analyse(np_data: np.ndarray, np_date: np.ndarray) -> None: percent_age_mean = np.round( np.nanmean(np_percent_age[:, age_group.value]), 2 ) - print( - f"age: {age_group.label} - field: {field.label} = {percent_age_mean}%" - ) + print(f"age: {age_group.label} - field: {field.name} = {percent_age_mean}%") percent_age_vac_mean = np.round( np.nanmean(np_percent_age_vac[:, age_group.value]), 2 ) print( - f"age: {age_group.label} - status: vac - field: {field.label} = {percent_age_vac_mean}%" + f"age: {age_group.label} - status: vac - field: {field.name} = {percent_age_vac_mean}%" ) percent_age_unvac_mean = np.round( np.nanmean(np_percent_age_unvac[:, age_group.value]), 2 ) print( - f"age: {age_group.label} - status: unvac - field: {field.label} = {percent_age_unvac_mean}%" + f"age: {age_group.label} - status: unvac - field: {field.name} = {percent_age_unvac_mean}%" ) @@ -324,7 +326,8 @@ def plot_bar_age_percent_vac_status_by_field( ) -> None: fig, ax = get_plot_fig(figsize=(22, 8), locator=md.WeekdayLocator()) bottom = np_data_vac_status[:, 0] - title = "vac" if is_vac else "no vac" + suffix = "vac" if is_vac else "unvac" + title = "Vaccinés" if is_vac else "Non vaccinés" for age_group in AgeGroup: percents_age = np_data_vac_status[:, age_group.value] if age_group.value > 0: @@ -351,7 +354,7 @@ def plot_bar_age_percent_vac_status_by_field( ) save_and_close_fig( fig, - os.path.join(OUTPUT_REPOSITORY, f"age_percent_{title}_{field.label}"), + os.path.join(OUTPUT_REPOSITORY, f"age_percent_{suffix}_{field.name.lower()}"), has_legend=False, ) @@ -385,13 +388,13 @@ def plot_cumulative_field( np_cumulate_unvac: np.ndarray = np.cumsum( np_data_unvac[:, age_group.value, field.value], axis=0 ) - plt.plot(np_date, np_cumulate_vac, label=f"{age_group.label} vac") - plt.plot(np_date, np_cumulate_unvac, label=f"{age_group.label} no vac") + plt.plot(np_date, np_cumulate_vac, label=f"{age_group.label} Vaccinés") + plt.plot(np_date, np_cumulate_unvac, label=f"{age_group.label} Non vaccinés") - plt.title(f"nombre de {field.label} cumulé par age") + plt.title(f"{field.label} cumulés par âge") plt.xlabel("date") save_and_close_fig( - fig, os.path.join(OUTPUT_REPOSITORY, f"cumulative_{field.label}") + fig, os.path.join(OUTPUT_REPOSITORY, f"cumulative_{field.name.lower()}") ) @@ -411,11 +414,13 @@ def plot_fields_by_age_vac( ) plt.xlabel("date") plt.ylabel("nombre") - plt.title(f"{age_group.label}ans - {vac_status.label}") + plt.title(f"{age_group.label} - {vac_status.label}") save_and_close_fig( fig, - os.path.join(OUTPUT_REPOSITORY, f"all_{age_group.label}_{vac_status.label}"), + os.path.join( + OUTPUT_REPOSITORY, f"all_{age_group.name.lower()}_{vac_status.name.lower()}" + ), ) @@ -440,8 +445,10 @@ def plot_bar_data_by_age_field( unvac_percent = np.round( np_percent_unvac[idx_date, age_group.value, field.value] * 100, 2 ) - bar_vac = ax.bar(idx_date, vac_percent, color="b", label="vac") - ax.bar(idx_date, unvac_percent, bottom=vac_percent, color="r", label="no vac") + bar_vac = ax.bar(idx_date, vac_percent, color="b", label="Vaccinés") + ax.bar( + idx_date, unvac_percent, bottom=vac_percent, color="r", label="Non vaccinés" + ) ax.bar_label( bar_vac, label_type="edge", color="black", fontsize="7", fmt="%.0f" ) @@ -458,11 +465,14 @@ def plot_bar_data_by_age_field( for idx, d in enumerate(np_date.astype(dt)) ], ) - plt.legend(["vac", "no vac"], loc="upper right", frameon=True) + plt.legend(["Vaccinés", "Non vaccinés"], loc="upper right", frameon=True) save_and_close_fig( fig, - os.path.join(OUTPUT_REPOSITORY, f"vac_percent_{age_group.label}_{field.label}"), + os.path.join( + OUTPUT_REPOSITORY, + f"vac_percent_{age_group.name.lower()}_{field.name.lower()}", + ), has_legend=False, ) @@ -489,6 +499,27 @@ def get_age_field_args() -> List[Tuple[AgeGroup, Field]]: return pool_args +def generate_html_page() -> None: + logging.info("generating html page with plots...") + os.makedirs(BUILD_REPOSITORY, exist_ok=True) + env = Environment( + loader=FileSystemLoader("templates"), autoescape=select_autoescape() + ) + template = env.get_template("index.template.html") + data = template.render( + **{ + "fields": Field, + "ages": AgeGroup, + "status": VacStatus, + "static": os.path.join(MAIN_URL, STATIC_REPOSITORY), + "src": DATA_URL, + } + ) + with open(os.path.join(BUILD_REPOSITORY, "index.html"), "w") as f: + f.write(data) + logging.info("html page build") + + if __name__ == "__main__": """ This script aims to plot DRESS data @@ -517,13 +548,18 @@ if __name__ == "__main__": "--no-plot", action="store_true", default=False, - help="redownload data for updates", + help="no plot data", + ) + parser.add_argument( + "-th", + "--to-html", + action="store_true", + default=False, + help="create an html with the plots", ) args = parser.parse_args() - os.makedirs(OUTPUT_REPOSITORY, exist_ok=True) - dic_data: Dict[str, Any] = get_data( file_path=os.path.join(DATA_REPOSITORY, "dress.json"), refresh=args.refresh ) @@ -533,6 +569,7 @@ if __name__ == "__main__": analyse(np_data, np_date) if not args.no_plot: + os.makedirs(OUTPUT_REPOSITORY, exist_ok=True) plot_fields_args = get_age_vac_args() f_fields = partial(plot_fields_by_age_vac, np_data, np_date) plot_vac_percent_age_args = get_age_field_args() @@ -544,3 +581,6 @@ if __name__ == "__main__": for field in Field: plot_cumulative_field(np_data, np_date, field) plot_bar_age_percent_by_field(np_data, np_date, field) + + if args.to_html: + generate_html_page() diff --git a/static/css/index.css b/static/css/index.css new file mode 100644 index 0000000..55e4ff5 --- /dev/null +++ b/static/css/index.css @@ -0,0 +1,31 @@ +.main-container { + display: flex; + flex-direction: column; + flex-wrap: nowrap; + justify-content: center; + text-align: center; +} + +.plot-container { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + flex-wrap: wrap; +} + +.plot-item { + flex: 0 1 33% +} + +.img-bars { + width: 1500px; + height: 500px; + border: solid +} + +img { + width: 100%; + height: 100%; + object-fit: contain; +} \ No newline at end of file diff --git a/templates/index.template.html b/templates/index.template.html new file mode 100644 index 0000000..bb7b070 --- /dev/null +++ b/templates/index.template.html @@ -0,0 +1,45 @@ + + + + Covid data extraction from DRESS dataset + + + + + + +
+

Thegux covid-19

+
source : Data DRESS
+

Hospitalisations/Soins critiques/Décés par status vaccinal groupés par tranches d'âges

+ {% for field in fields %} + + + {% endfor %} +

Hospitalisations/Soins critiques/Décés par tranches d'âges groupés par status vaccinal

+ {% for age in ages %} + {% for field in fields %} + + {% endfor %} + {% endfor %} +

Hospitalisations/Soins critiques/Décés par tranches d'âges et status vaccinal

+
+ {% for age in ages %} + {% for vac_status in status %} +
+ +
+ {% endfor %} + {% endfor %} +
+

Hospitalisations/Soins critiques/Décés cumulés par tranches d'âges et status vaccinal

+
+ {% for field in fields %} +
+ +
+ {% endfor %} +
+
+ + \ No newline at end of file