mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
add chart of version support to version.rst
This commit is contained in:
parent
9e099f97d9
commit
7a9abe3ec5
@ -46,7 +46,7 @@ variables:
|
|||||||
CI_TARGET_TEST_CONFIG_FILE: "$CI_PROJECT_DIR/tools/ci/config/target-test.yml"
|
CI_TARGET_TEST_CONFIG_FILE: "$CI_PROJECT_DIR/tools/ci/config/target-test.yml"
|
||||||
|
|
||||||
# Versioned esp-idf-doc env image to use for all document building jobs
|
# Versioned esp-idf-doc env image to use for all document building jobs
|
||||||
ESP_IDF_DOC_ENV_IMAGE: "$CI_DOCKER_REGISTRY/esp-idf-doc-env:v5"
|
ESP_IDF_DOC_ENV_IMAGE: "$CI_DOCKER_REGISTRY/esp-idf-doc-env:v6"
|
||||||
|
|
||||||
|
|
||||||
# before each job, we need to check if this job is filtered by bot stage/job filter
|
# before each job, we need to check if this job is filtered by bot stage/job filter
|
||||||
|
@ -25,7 +25,7 @@ reserved for features which are needed to meet particular regulatory
|
|||||||
requirements or standards for new products, and which carry a very low risk of
|
requirements or standards for new products, and which carry a very low risk of
|
||||||
introducing regressions.)
|
introducing regressions.)
|
||||||
|
|
||||||
During the Maintenace period, the version is still supported but only bugfixes
|
During the Maintenance period, the version is still supported but only bugfixes
|
||||||
for high severity issues or security issues will be applied.
|
for high severity issues or security issues will be applied.
|
||||||
|
|
||||||
Using an “In Service” version is recommended when starting a new project.
|
Using an “In Service” version is recommended when starting a new project.
|
||||||
|
BIN
docs/chart.png
BIN
docs/chart.png
Binary file not shown.
Before Width: | Height: | Size: 62 KiB |
@ -54,6 +54,7 @@ extensions = ['breathe',
|
|||||||
'sphinxcontrib.nwdiag',
|
'sphinxcontrib.nwdiag',
|
||||||
'sphinxcontrib.rackdiag',
|
'sphinxcontrib.rackdiag',
|
||||||
'sphinxcontrib.packetdiag',
|
'sphinxcontrib.packetdiag',
|
||||||
|
'sphinxcontrib.cairosvgconverter',
|
||||||
|
|
||||||
'extensions.html_redirects',
|
'extensions.html_redirects',
|
||||||
'extensions.toctree_filter',
|
'extensions.toctree_filter',
|
||||||
|
@ -70,7 +70,49 @@ As a general guideline:
|
|||||||
- If possible, periodically update the project to a new major or minor ESP-IDF version (for example, once a year.) The update process should be straightforward for Minor updates, but may require some planning and checking of the release notes for Major updates.
|
- If possible, periodically update the project to a new major or minor ESP-IDF version (for example, once a year.) The update process should be straightforward for Minor updates, but may require some planning and checking of the release notes for Major updates.
|
||||||
- Always plan to update to a newer release before the release you are using becomes End of Life.
|
- Always plan to update to a newer release before the release you are using becomes End of Life.
|
||||||
|
|
||||||
.. image:: ../chart.png
|
Each ESP-IDF major and minor release (V4.1, V4.2, etc) is supported for
|
||||||
|
30 months after the initial stable release date.
|
||||||
|
|
||||||
|
Supported means that the ESP-IDF team will continue to apply bug fixes,
|
||||||
|
security fixes, etc to the release branch on GitHub, and periodically
|
||||||
|
make new bugfix releases as needed.
|
||||||
|
|
||||||
|
Support period is divided into "Service" and "Maintenance" period:
|
||||||
|
|
||||||
|
+-------------+--------------+---------------------------------------+
|
||||||
|
| Period | Duration | Recommended for new projects? |
|
||||||
|
+=============+==============+=======================================+
|
||||||
|
| Service | 12 months | Yes |
|
||||||
|
+-------------+--------------+---------------------------------------+
|
||||||
|
| Maintenance | 18 months | No |
|
||||||
|
+-------------+--------------+---------------------------------------+
|
||||||
|
|
||||||
|
During the Service period, bugfixes releases are more frequent. In some cases,
|
||||||
|
support for new features may be added during the Service period (this is
|
||||||
|
reserved for features which are needed to meet particular regulatory
|
||||||
|
requirements or standards for new products, and which carry a very low risk of
|
||||||
|
introducing regressions.)
|
||||||
|
|
||||||
|
During the Maintenance period, the version is still supported but only bugfixes
|
||||||
|
for high severity issues or security issues will be applied.
|
||||||
|
|
||||||
|
Using an “In Service” version is recommended when starting a new project.
|
||||||
|
|
||||||
|
Users are encouraged to upgrade all projects to a newer ESP-IDF release before
|
||||||
|
the support period finishes and the release becomes End of Life (EOL). It is our
|
||||||
|
policy to not continue fixing bugs in End of Life releases.
|
||||||
|
|
||||||
|
Pre-release versions (betas, previews, `-rc` and `-dev` versions, etc)
|
||||||
|
are not covered by any support period. Sometimes a particular feature is
|
||||||
|
marked as "Preview" in a release, which means it is also not covered
|
||||||
|
by the support period.
|
||||||
|
|
||||||
|
The ESP-IDF Programming Guide has information about the
|
||||||
|
`different versions of ESP-IDF <https://docs.espressif.com/projects/esp-idf/en/latest/versions.html>`_
|
||||||
|
(major, minor, bugfix, etc).
|
||||||
|
|
||||||
|
|
||||||
|
.. image:: https://dl.espressif.com/dl/esp-idf/support-periods.svg
|
||||||
|
|
||||||
|
|
||||||
Checking the Current Version
|
Checking the Current Version
|
||||||
|
216
docs/generate_chart.py
Normal file
216
docs/generate_chart.py
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
import argparse
|
||||||
|
import datetime as dt
|
||||||
|
import json
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import requests
|
||||||
|
import matplotlib.dates
|
||||||
|
import matplotlib.patches as mpatches
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from matplotlib.dates import MONTHLY, DateFormatter, RRuleLocator, rrulewrapper
|
||||||
|
from dateutil import parser
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
|
|
||||||
|
class Version(object):
|
||||||
|
def __init__(self, version_name, explicit_start_date, explicit_end_date, explicit_end_service_date=None):
|
||||||
|
self.version_name = version_name
|
||||||
|
|
||||||
|
self._start_date = parser.parse(explicit_start_date)
|
||||||
|
self._end_of_life_date = parser.parse(explicit_end_date)
|
||||||
|
self._end_service_date = parser.parse(
|
||||||
|
explicit_end_service_date) if explicit_end_service_date is not None else self.compute_end_service_date()
|
||||||
|
|
||||||
|
self.start_date_matplotlib_format = matplotlib.dates.date2num(self._start_date)
|
||||||
|
self.end_of_life_date_matplotlib_format = matplotlib.dates.date2num(self._end_of_life_date)
|
||||||
|
|
||||||
|
self.end_service_date_matplotlib_format = matplotlib.dates.date2num(self._end_service_date)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_months(source_date, months):
|
||||||
|
return source_date + relativedelta(months=+months)
|
||||||
|
|
||||||
|
def get_start_date(self):
|
||||||
|
return self._start_date
|
||||||
|
|
||||||
|
def get_end_of_life_date(self):
|
||||||
|
return self._end_of_life_date
|
||||||
|
|
||||||
|
def get_end_service_date(self):
|
||||||
|
return self._end_service_date
|
||||||
|
|
||||||
|
def compute_end_service_date(self):
|
||||||
|
return self.add_months(self._start_date, 12)
|
||||||
|
|
||||||
|
|
||||||
|
class ChartVersions(object):
|
||||||
|
def __init__(self, url=None, filename=None):
|
||||||
|
self._releases = self._get_releases_from_url(url=url, filename=filename)
|
||||||
|
self.sorted_releases_supported = sorted(self.filter_old_versions(self._releases), key=lambda x: x.version_name,
|
||||||
|
reverse=True)
|
||||||
|
|
||||||
|
def get_releases_as_json(self):
|
||||||
|
return {
|
||||||
|
x.version_name: {
|
||||||
|
"start_date": x.get_start_date().strftime("%Y-%m-%d"),
|
||||||
|
"end_service": x.get_end_service_date().strftime("%Y-%m-%d"),
|
||||||
|
"end_date": x.get_end_of_life_date().strftime("%Y-%m-%d")
|
||||||
|
} for x in self.sorted_releases_supported
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_chart_releases_from_js(js_as_string):
|
||||||
|
return json.loads(js_as_string[js_as_string.find("RELEASES: ") + len("RELEASES: "):js_as_string.rfind("};")])
|
||||||
|
|
||||||
|
def _get_all_version_from_url(self, url=None, filename=None):
|
||||||
|
releases_file = requests.get(url).text if url is not None else "".join(open(filename).readlines())
|
||||||
|
return self.parse_chart_releases_from_js(releases_file)
|
||||||
|
|
||||||
|
def _get_releases_from_url(self, url=None, filename=None):
|
||||||
|
all_versions = self._get_all_version_from_url(url, filename)
|
||||||
|
return [
|
||||||
|
Version(version_name=x,
|
||||||
|
explicit_start_date=all_versions[x]['start_date'],
|
||||||
|
explicit_end_date=all_versions[x]['end_date'] if 'end_date' in all_versions[x].keys() else None,
|
||||||
|
explicit_end_service_date=all_versions[x]['end_service'] if 'end_service' in all_versions[
|
||||||
|
x].keys() else None)
|
||||||
|
for x in all_versions.keys()
|
||||||
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def filter_old_versions(versions):
|
||||||
|
return list(
|
||||||
|
filter(lambda x: x.get_end_of_life_date() >= dt.datetime.now(x.get_end_of_life_date().tzinfo), versions))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def months_timedelta(datetime_1, datetime2):
|
||||||
|
datetime_1, datetime2 = (datetime2, datetime_1) if datetime_1 > datetime2 else (datetime_1, datetime2)
|
||||||
|
return (datetime2.year * 12 + datetime2.month) - (datetime_1.year * 12 + datetime_1.month)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def find_next_multiple_of_power_two(number, initial=3):
|
||||||
|
"""
|
||||||
|
Computes the next multiple of the number by some power of two.
|
||||||
|
>>> ChartVersions.find_next_multiple_of_power_two(7, 3)
|
||||||
|
12
|
||||||
|
"""
|
||||||
|
msb = number.bit_length()
|
||||||
|
return 3 if number <= 1 else initial << msb - 2 << (1 & number >> msb - 2)
|
||||||
|
|
||||||
|
def find_nearest_multiple_of_power_two(self, number, initial=3, prefer_next=False):
|
||||||
|
next_num = self.find_next_multiple_of_power_two(number=number - 1, initial=initial)
|
||||||
|
previous_num = next_num >> 1
|
||||||
|
return next_num if abs(next_num - number) < (abs(previous_num - number) + int(prefer_next)) else previous_num
|
||||||
|
|
||||||
|
def create_chart(self,
|
||||||
|
figure_size=(41.8330013267, 16.7332005307),
|
||||||
|
subplot=111,
|
||||||
|
step_size=0.5,
|
||||||
|
bar_height=0.3,
|
||||||
|
version_alpha=0.8,
|
||||||
|
lts_service_color='darkred',
|
||||||
|
lts_maintenance_color='red',
|
||||||
|
bar_align='center',
|
||||||
|
date_interval=None,
|
||||||
|
output_chart_name='docs/chart',
|
||||||
|
output_chart_extension='.png',
|
||||||
|
months_surrounding_chart=4,
|
||||||
|
service_period_label='Service period (Recommended for new designs)',
|
||||||
|
maintenance_period_text='Maintenance period'):
|
||||||
|
fig = plt.figure(figsize=figure_size)
|
||||||
|
ax = fig.add_subplot(subplot)
|
||||||
|
|
||||||
|
labels_count = len(self.sorted_releases_supported)
|
||||||
|
|
||||||
|
pos = np.arange(step_size, labels_count * step_size + step_size, step_size)
|
||||||
|
|
||||||
|
for release, i in zip(self.sorted_releases_supported, range(labels_count)):
|
||||||
|
start_date = release.start_date_matplotlib_format
|
||||||
|
end_of_service_date = release.end_service_date_matplotlib_format
|
||||||
|
|
||||||
|
end_date = release.end_of_life_date_matplotlib_format
|
||||||
|
|
||||||
|
ax.barh((i * step_size) + step_size, (end_of_service_date or end_date) - start_date, left=start_date,
|
||||||
|
height=bar_height, align=bar_align,
|
||||||
|
color=lts_service_color,
|
||||||
|
alpha=version_alpha,
|
||||||
|
edgecolor=lts_service_color)
|
||||||
|
if end_of_service_date is not None:
|
||||||
|
ax.barh((i * step_size) + step_size, end_date - end_of_service_date, left=end_of_service_date,
|
||||||
|
height=bar_height, align=bar_align,
|
||||||
|
color=lts_maintenance_color, alpha=version_alpha, edgecolor=lts_maintenance_color)
|
||||||
|
|
||||||
|
ax.set_ylim(bottom=0, ymax=labels_count * step_size + step_size)
|
||||||
|
|
||||||
|
max_ax_date = Version.add_months(
|
||||||
|
max(self.sorted_releases_supported,
|
||||||
|
key=lambda version: version.get_end_of_life_date().replace(tzinfo=None)).get_end_of_life_date(),
|
||||||
|
months_surrounding_chart + 1).replace(day=1)
|
||||||
|
|
||||||
|
min_ax_date = Version.add_months(
|
||||||
|
min(self.sorted_releases_supported,
|
||||||
|
key=lambda version: version.get_start_date().replace(tzinfo=None)).get_start_date(),
|
||||||
|
-months_surrounding_chart).replace(day=1)
|
||||||
|
|
||||||
|
x_ax_interval = date_interval or self.find_nearest_multiple_of_power_two(
|
||||||
|
self.months_timedelta(max_ax_date, min_ax_date) // 10)
|
||||||
|
|
||||||
|
ax.set_xlim(xmin=min_ax_date, xmax=max_ax_date)
|
||||||
|
|
||||||
|
ax.grid(color='g', linestyle=':')
|
||||||
|
ax.xaxis_date()
|
||||||
|
|
||||||
|
rule = rrulewrapper(MONTHLY, interval=x_ax_interval)
|
||||||
|
loc = RRuleLocator(rule)
|
||||||
|
formatter = DateFormatter("%b %Y")
|
||||||
|
|
||||||
|
ax.xaxis.set_major_locator(loc)
|
||||||
|
ax.xaxis.set_major_formatter(formatter)
|
||||||
|
x_labels = ax.get_xticklabels()
|
||||||
|
plt.ylabel('ESP-IDF Release', size=12)
|
||||||
|
|
||||||
|
ax.invert_yaxis()
|
||||||
|
fig.autofmt_xdate()
|
||||||
|
|
||||||
|
darkred_patch = mpatches.Patch(color=lts_service_color, label=service_period_label)
|
||||||
|
red_patch = mpatches.Patch(color=lts_maintenance_color, label=maintenance_period_text)
|
||||||
|
|
||||||
|
plt.setp(plt.yticks(pos, map(lambda x: x.version_name, self.sorted_releases_supported))[1], rotation=0,
|
||||||
|
fontsize=10, family='Tahoma')
|
||||||
|
plt.setp(x_labels, rotation=30, fontsize=11, family='Tahoma')
|
||||||
|
plt.legend(handles=[darkred_patch, red_patch], prop={'size': 10, 'family': 'Tahoma'},
|
||||||
|
bbox_to_anchor=(1.01, 1.165), loc='upper right')
|
||||||
|
fig.set_size_inches(11, 5, forward=True)
|
||||||
|
plt.savefig(output_chart_name + output_chart_extension, bbox_inches='tight')
|
||||||
|
print("Saved into " + output_chart_name + output_chart_extension)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
arg_parser = argparse.ArgumentParser(
|
||||||
|
description="Create chart of version support. Set the url or filename with versions."
|
||||||
|
"If you set both filename and url the script will prefer filename.")
|
||||||
|
arg_parser.add_argument("--url", metavar="URL", default="https://dl.espressif.com/dl/esp-idf/idf_versions.js")
|
||||||
|
arg_parser.add_argument("--filename",
|
||||||
|
help="Set the name of the source file, if is set, the script ignores the url.")
|
||||||
|
arg_parser.add_argument("--output-format", help="Set the output format of the image.", default="svg")
|
||||||
|
arg_parser.add_argument("--output-file", help="Set the name of the output file.", default="docs/chart")
|
||||||
|
args = arg_parser.parse_args()
|
||||||
|
|
||||||
|
ChartVersions(url=args.url if args.filename is None else None, filename=args.filename).create_chart(
|
||||||
|
output_chart_extension="." + args.output_format.lower()[-3:], output_chart_name=args.output_file)
|
@ -1,6 +1,10 @@
|
|||||||
# This is a list of python packages used to generate documentation. This file is used with pip:
|
# This is a list of python packages used to generate documentation. This file is used with pip:
|
||||||
# pip install --user -r requirements.txt
|
# pip install --user -r requirements.txt
|
||||||
#
|
#
|
||||||
|
# matplotlib is currently required only by the script generate_chart.py
|
||||||
|
matplotlib==3.3.1 ; python_version>="3"
|
||||||
|
matplotlib==2.0.1 ; python_version=="2.7"
|
||||||
|
cairosvg==2.4.2 # required by sphinxcontrib-svg2pdfconverter[CairoSVG]
|
||||||
sphinx==2.3.1
|
sphinx==2.3.1
|
||||||
breathe==4.14.1
|
breathe==4.14.1
|
||||||
sphinx-copybutton==0.3.0
|
sphinx-copybutton==0.3.0
|
||||||
@ -10,6 +14,7 @@ sphinxcontrib-seqdiag==2.0.0
|
|||||||
sphinxcontrib-actdiag==2.0.0
|
sphinxcontrib-actdiag==2.0.0
|
||||||
sphinxcontrib-nwdiag==2.0.0
|
sphinxcontrib-nwdiag==2.0.0
|
||||||
sphinxcontrib-wavedrom==2.0.0
|
sphinxcontrib-wavedrom==2.0.0
|
||||||
|
sphinxcontrib-svg2pdfconverter[CairoSVG]==1.1.0
|
||||||
nwdiag==2.0.0
|
nwdiag==2.0.0
|
||||||
recommonmark
|
recommonmark
|
||||||
future>=0.16.0 # for ../tools/gen_esp_err_to_name.py
|
future>=0.16.0 # for ../tools/gen_esp_err_to_name.py
|
||||||
|
@ -1,265 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
import datetime as dt
|
|
||||||
import requests
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
|
|
||||||
import matplotlib.dates
|
|
||||||
import matplotlib.font_manager as font_manager
|
|
||||||
import matplotlib.patches as mpatches
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
import numpy as np
|
|
||||||
from dateutil import parser
|
|
||||||
from dateutil.relativedelta import relativedelta
|
|
||||||
from matplotlib.dates import WEEKLY, DateFormatter, RRuleLocator, rrulewrapper
|
|
||||||
|
|
||||||
|
|
||||||
class Version(object):
|
|
||||||
def __init__(self, version_name,
|
|
||||||
explicit_start_date=None,
|
|
||||||
explicit_end_date=None,
|
|
||||||
explicit_end_service_date=None,
|
|
||||||
is_lts=None):
|
|
||||||
self.version_name = version_name
|
|
||||||
|
|
||||||
self.is_lts = is_lts or self.is_version_lts()
|
|
||||||
|
|
||||||
self.is_major_minor = Version.is_minor_major_version(self.version_name)
|
|
||||||
|
|
||||||
self._start_date = parser.parse(
|
|
||||||
explicit_start_date) if explicit_start_date is not None else self._retrieve_start_date()
|
|
||||||
self._end_of_life_date = parser.parse(
|
|
||||||
explicit_end_date) if explicit_end_date is not None else self._retrieve_end_of_life_date()
|
|
||||||
self._end_service_date = parser.parse(
|
|
||||||
explicit_end_service_date) if explicit_end_service_date is not None else self.get_end_service_date()
|
|
||||||
|
|
||||||
self.start_date_matplotlib_format = matplotlib.dates.date2num(self._start_date)
|
|
||||||
self.end_of_life_date_matplotlib_format = matplotlib.dates.date2num(self._end_of_life_date)
|
|
||||||
|
|
||||||
self.end_service_date_matplotlib_format = matplotlib.dates.date2num(
|
|
||||||
self._end_service_date) if self._end_service_date is not None else None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_config(config_path):
|
|
||||||
return json.load(open(config_path))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def add_months(source_date, months):
|
|
||||||
return source_date + relativedelta(months=+months)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def is_minor_major_version(version_name):
|
|
||||||
return True if len(version_name.split(".")) <= 2 else False
|
|
||||||
|
|
||||||
def is_version_lts(self):
|
|
||||||
version = self.version_name
|
|
||||||
return version >= 'v4.1'
|
|
||||||
|
|
||||||
def get_start_date(self):
|
|
||||||
return self._start_date
|
|
||||||
|
|
||||||
def get_end_of_life_date(self):
|
|
||||||
return self._end_of_life_date
|
|
||||||
|
|
||||||
def _retrieve_start_date(self):
|
|
||||||
return parser.parse(os.popen("git log -1 --format=%ai " + self.version_name))
|
|
||||||
|
|
||||||
def _retrieve_end_of_life_date(self):
|
|
||||||
return self.add_months(self._start_date, 30 if self.is_lts else 18)
|
|
||||||
|
|
||||||
def get_end_service_date(self):
|
|
||||||
return self.add_months(self._start_date, 12)
|
|
||||||
|
|
||||||
|
|
||||||
class ChartVersions(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.all_versions = self.get_all_versions_from_git()
|
|
||||||
|
|
||||||
self._releases = self._get_releases_from_url(url="https://dl.espressif.com/dl/esp-idf/idf_versions.js")
|
|
||||||
self._patches = self._get_patches_from_url(url="https://dl.espressif.com/dl/esp-idf/idf_versions.js")
|
|
||||||
|
|
||||||
self.sorted_releases_supported = sorted(self.filter_old_versions(self._releases), key=lambda x: x.version_name)
|
|
||||||
self.patches_supported = self.filter_old_versions(self._patches)
|
|
||||||
|
|
||||||
# TODO test higher versions
|
|
||||||
# self.sorted_releases_supported.append(Version(version_name='v4.1', explicit_start_date="28-9-2020"))
|
|
||||||
# self.sorted_releases_supported.append(Version(version_name='v4.2', explicit_start_date="28-5-2021"))
|
|
||||||
|
|
||||||
def get_releases_as_json(self):
|
|
||||||
return {
|
|
||||||
x.version_name: {
|
|
||||||
"start_date": x.get_start_date().strftime("%Y-%m-%d"),
|
|
||||||
"end_date": x.get_end_of_life_date().strftime("%Y-%m-%d"),
|
|
||||||
"is_lts": x.is_lts
|
|
||||||
} for x in self.sorted_releases_supported + self.patches_supported
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def parse_chart_releases_from_js(js_as_string):
|
|
||||||
return json.loads(js_as_string[js_as_string.find("RELEASES: ") + len("RELEASES: "):js_as_string.rfind("};")])
|
|
||||||
|
|
||||||
def _get_all_version_from_url(self, url=None, filename=None):
|
|
||||||
releases_file = requests.get(url).text if url is not None else "".join(open(filename).readlines())
|
|
||||||
return self.parse_chart_releases_from_js(releases_file)
|
|
||||||
|
|
||||||
def _get_releases_from_url(self, url=None, filename=None):
|
|
||||||
all_versions = self._get_all_version_from_url(url, filename)
|
|
||||||
return [
|
|
||||||
Version(version_name=x,
|
|
||||||
explicit_start_date=all_versions[x]['start_date'],
|
|
||||||
explicit_end_date=all_versions[x]['end_date'] if 'end_date' in all_versions[x].keys() else None,
|
|
||||||
explicit_end_service_date=all_versions[x]['end_service'] if 'end_service' in all_versions[x].keys() else None)
|
|
||||||
for x in all_versions.keys() if Version.is_minor_major_version(x)
|
|
||||||
]
|
|
||||||
|
|
||||||
def _get_patches_from_url(self, url=None, filename=None):
|
|
||||||
all_versions = self._get_all_version_from_url(url, filename)
|
|
||||||
return [
|
|
||||||
Version(version_name=x,
|
|
||||||
explicit_start_date=all_versions[x]['start_date'],
|
|
||||||
explicit_end_date=all_versions[x]['end_date'] if 'end_date' in all_versions[x].keys() else None,
|
|
||||||
is_lts=all_versions[x]['new_policy'] if 'new_policy' in all_versions[x].keys() else None)
|
|
||||||
for x in all_versions.keys() if not Version.is_minor_major_version(x)
|
|
||||||
]
|
|
||||||
|
|
||||||
def _get_releases(self, all_versions):
|
|
||||||
return [Version(release) for release in self._get_releases_names(all_versions)]
|
|
||||||
|
|
||||||
def _get_patches(self, all_versions):
|
|
||||||
return [Version(patch, explicit_end_date=self.get_super_end_date_as_string(patch),
|
|
||||||
is_lts=self.get_super_lts_state(patch)) for patch in self._get_patches_names(all_versions)]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _get_releases_names(all_versions):
|
|
||||||
return list(filter(lambda x: Version.is_minor_major_version(x), all_versions))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _get_patches_names(all_versions):
|
|
||||||
return list(filter(lambda x: not Version.is_minor_major_version(x), all_versions))
|
|
||||||
|
|
||||||
def get_super_version(self, patch):
|
|
||||||
return list(filter(lambda x: x.version_name == self.get_super_version_name(patch), self._releases))[0]
|
|
||||||
|
|
||||||
def get_super_end_date_as_string(self, patch):
|
|
||||||
return self.get_super_version(patch).get_end_of_life_date().strftime("%m/%d/%Y, %H:%M:%S")
|
|
||||||
|
|
||||||
def get_super_lts_state(self, patch):
|
|
||||||
return self.get_super_version(patch).is_lts
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_all_versions_from_git():
|
|
||||||
"""
|
|
||||||
:returns: list of string variables meaning name of the versions or patches (e.g. v4.1, v3.3.1, etc.)
|
|
||||||
"""
|
|
||||||
all_git_tags = os.popen("git tag")
|
|
||||||
stable_releases_regex = "(?:^|)(v\d+\.\d+(?:\.\d+){0,1})(?=\n|$)"
|
|
||||||
|
|
||||||
results = [re.match(stable_releases_regex, line) for line in all_git_tags]
|
|
||||||
all_versions = [regex_result.group(1) for regex_result in results if regex_result is not None]
|
|
||||||
return all_versions
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def filter_old_versions(versions):
|
|
||||||
return list(
|
|
||||||
filter(lambda x: x.get_end_of_life_date() >= dt.datetime.now(x.get_end_of_life_date().tzinfo), versions))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_super_version_name(version_name):
|
|
||||||
return ".".join(version_name.split(".")[:2])
|
|
||||||
|
|
||||||
def create_chart(self,
|
|
||||||
figure_size=(20, 8),
|
|
||||||
subplot=111,
|
|
||||||
step_size=0.5,
|
|
||||||
bar_height=0.3,
|
|
||||||
version_alpha=0.8,
|
|
||||||
patch_width=1,
|
|
||||||
lts_service_color='darkred',
|
|
||||||
lts_maintenance_color='red',
|
|
||||||
bar_align='center',
|
|
||||||
patch_color='black',
|
|
||||||
patch_alpha=1,
|
|
||||||
date_interval=10,
|
|
||||||
output_chart_name='docs/chart',
|
|
||||||
output_chart_extension='.png',
|
|
||||||
months_surrounding_chart=5):
|
|
||||||
fig = plt.figure(figsize=figure_size)
|
|
||||||
ax = fig.add_subplot(subplot)
|
|
||||||
|
|
||||||
labels_count = len(self.sorted_releases_supported)
|
|
||||||
|
|
||||||
pos = np.arange(step_size, labels_count * step_size + step_size, step_size)
|
|
||||||
mapping_releases_index = {release.version_name: i for release, i in
|
|
||||||
zip(self.sorted_releases_supported, range(labels_count))}
|
|
||||||
|
|
||||||
for release, i in zip(self.sorted_releases_supported, range(labels_count)):
|
|
||||||
start_date = release.start_date_matplotlib_format
|
|
||||||
end_of_service_date = release.end_service_date_matplotlib_format
|
|
||||||
|
|
||||||
end_date = release.end_of_life_date_matplotlib_format
|
|
||||||
|
|
||||||
ax.barh((i * step_size) + step_size, (end_of_service_date or end_date) - start_date, left=start_date,
|
|
||||||
height=bar_height, align=bar_align,
|
|
||||||
color=lts_service_color,
|
|
||||||
alpha=version_alpha,
|
|
||||||
edgecolor=lts_service_color)
|
|
||||||
if end_of_service_date is not None:
|
|
||||||
ax.barh((i * step_size) + step_size, end_date - end_of_service_date, left=end_of_service_date,
|
|
||||||
height=bar_height, align=bar_align,
|
|
||||||
color=lts_maintenance_color, alpha=version_alpha, edgecolor=lts_maintenance_color)
|
|
||||||
|
|
||||||
for patch, i in zip(self.patches_supported, range(len(self.patches_supported))):
|
|
||||||
start_date = patch.start_date_matplotlib_format
|
|
||||||
|
|
||||||
ax.barh(mapping_releases_index[self.get_super_version_name(patch.version_name)] * step_size + step_size,
|
|
||||||
patch_width, left=start_date, height=bar_height, align=bar_align, color=patch_color,
|
|
||||||
alpha=patch_alpha,
|
|
||||||
edgecolor=patch_color)
|
|
||||||
ax.text(start_date - 5, mapping_releases_index[
|
|
||||||
self.get_super_version_name(patch.version_name)] * step_size + step_size - 0.2,
|
|
||||||
patch.version_name.split('.')[-1],
|
|
||||||
fontsize='xx-small', color='darkred')
|
|
||||||
|
|
||||||
plt.setp(plt.yticks(pos, map(lambda x: x.version_name, self.sorted_releases_supported))[1], fontsize=14)
|
|
||||||
|
|
||||||
ax.set_ylim(bottom=0, ymax=labels_count * step_size + step_size)
|
|
||||||
|
|
||||||
ax.set_xlim(
|
|
||||||
xmin=Version.add_months(
|
|
||||||
min(self.sorted_releases_supported,
|
|
||||||
key=lambda version: version.get_start_date().replace(tzinfo=None)).get_start_date(),
|
|
||||||
-months_surrounding_chart),
|
|
||||||
xmax=Version.add_months(
|
|
||||||
max(self.sorted_releases_supported,
|
|
||||||
key=lambda version: version.get_end_of_life_date().replace(tzinfo=None)).get_end_of_life_date(),
|
|
||||||
months_surrounding_chart))
|
|
||||||
|
|
||||||
ax.grid(color='g', linestyle=':')
|
|
||||||
ax.xaxis_date()
|
|
||||||
|
|
||||||
rule = rrulewrapper(WEEKLY, interval=date_interval)
|
|
||||||
loc = RRuleLocator(rule)
|
|
||||||
formatter = DateFormatter("%d-%b '%y")
|
|
||||||
|
|
||||||
ax.xaxis.set_major_locator(loc)
|
|
||||||
ax.xaxis.set_major_formatter(formatter)
|
|
||||||
x_labels = ax.get_xticklabels()
|
|
||||||
plt.setp(x_labels, rotation=30, fontsize=10)
|
|
||||||
|
|
||||||
ax.legend(loc=1, prop=font_manager.FontProperties(size='small'))
|
|
||||||
|
|
||||||
ax.invert_yaxis()
|
|
||||||
fig.autofmt_xdate()
|
|
||||||
|
|
||||||
darkred_patch = mpatches.Patch(color='darkred', label='Service period (Recommended for new designs)')
|
|
||||||
red_patch = mpatches.Patch(color='red', label='Maintenance period')
|
|
||||||
|
|
||||||
plt.legend(handles=[darkred_patch, red_patch], prop={'size': 6})
|
|
||||||
fig.set_size_inches(10, 5, forward=True)
|
|
||||||
plt.savefig(output_chart_name + output_chart_extension)
|
|
||||||
plt.show()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
ChartVersions().create_chart()
|
|
@ -1,96 +0,0 @@
|
|||||||
var DOCUMENTATION_VERSIONS = {
|
|
||||||
DEFAULTS: {
|
|
||||||
has_targets: false,
|
|
||||||
supported_targets: ["esp32"]
|
|
||||||
},
|
|
||||||
VERSIONS: [{
|
|
||||||
name: "latest",
|
|
||||||
has_targets: true,
|
|
||||||
supported_targets: ["esp32", "esp32s2"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v4.1-rc",
|
|
||||||
pre_release: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v4.0.1",
|
|
||||||
old: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v4.0",
|
|
||||||
old: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v3.3.2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v3.3.1",
|
|
||||||
old: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v3.3",
|
|
||||||
old: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v3.2.3",
|
|
||||||
old: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v3.1.7",
|
|
||||||
old: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v3.1.6",
|
|
||||||
old: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v3.0.9",
|
|
||||||
old: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "release-v4.2",
|
|
||||||
pre_release: true,
|
|
||||||
has_targets: true,
|
|
||||||
supported_targets: ["esp32", "esp32s2"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "release-v4.1",
|
|
||||||
pre_release: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "release-v4.0",
|
|
||||||
pre_release: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "release-v3.3",
|
|
||||||
pre_release: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "release-v3.2",
|
|
||||||
pre_release: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "release-v3.1",
|
|
||||||
pre_release: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
RELEASES: {
|
|
||||||
"v3.1": {
|
|
||||||
"start_date": "2018-09-07",
|
|
||||||
"end_date": "2020-10-31"
|
|
||||||
},
|
|
||||||
"v3.2": {
|
|
||||||
"start_date": "2019-04-11",
|
|
||||||
"end_date": "2020-10-31"
|
|
||||||
},
|
|
||||||
"v3.3": {
|
|
||||||
"start_date": "2019-09-05",
|
|
||||||
"end_date": "2022-03-31"
|
|
||||||
},
|
|
||||||
"v4.0": {
|
|
||||||
"start_date": "2020-02-11",
|
|
||||||
"end_service": "2021-02-11",
|
|
||||||
"end_date": "2021-10-31"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user