diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..609db7b --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,73 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at oss@maif.fr. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..49347e1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,77 @@ +# Contributing to meteole + +These guidelines apply to all your-project projects living in the the `MAIF/meteole` repository. + +These guidelines are meant to be a living document that should be changed and adapted as needed. +We encourage changes that make it easier to achieve our goals in an efficient way. + +## Codebase + +Explain the layout of your repo. + +## Workflow + +The steps below describe how to get a patch into a main development branch (e.g. `master`). +The steps are exactly the same for everyone involved in the project (be it core team, or first time contributor). +We follow the standard GitHub [fork & pull](https://help.github.com/articles/using-pull-requests/#fork--pull) approach to pull requests. Just fork the official repo, develop in a branch, and submit a PR! + +1. To avoid duplicated effort, it might be good to check the [issue tracker](https://github.com/MAIF/meteole/issues) and [existing pull requests](https://github.com/MAIF/meteole/pulls) for existing work. + - If there is no ticket yet, feel free to [create one](https://github.com/MAIF/meteole/issues/new) to discuss the problem and the approach you want to take to solve it. +1. [Fork the project](https://github.com/MAIF/meteole#fork-destination-box) on GitHub. You'll need to create a feature-branch for your work on your fork, as this way you'll be able to submit a pull request against the mainline *meteole*. +1. Create a branch on your fork and work on the feature. For example: `git checkout -b feature/awesome-new-feature` + - Please make sure to follow the general quality guidelines (specified below) when developing your patch. + - Please write additional tests covering your feature and adjust existing ones if needed before submitting your pull request. +1. Once your feature is complete, prepare the commit with a good commit message, for example: `Adding canary mode support for services #42` (note the reference to the ticket it aimed to resolve). +1. If it's a new feature, or a change of behaviour, document it on the [Meteole docs](https://maif.github.io/meteole/home/), remember, an undocumented feature is not a feature. +1. Now it's finally time to [submit the pull request](https://help.github.com/articles/using-pull-requests)! + - Please make sure to include a reference to the issue you're solving *in the comment* for the Pull Request, this will cause the PR to be linked properly with the Issue. Examples of good phrases for this are: "Resolves #1234" or "Refs #1234". +1. Now both committers and interested people will review your code. This process is to ensure the code we merge is of the best possible quality, and that no silly mistakes slip through. You're expected to follow-up these comments by adding new commits to the same branch. The commit messages of those commits can be more loose, for example: `Removed debugging using printline`, as they all will be squashed into one commit before merging into the main branch. + - The community and team are eager to share, so don't be afraid to ask follow up questions if you didn't understand some comment, or would like clarification on how to continue with a given feature. We're here to help, so feel free to ask and discuss any kind of questions you might have during review! +1. After the review you should fix the issues as needed (pushing a new commit for new review etc.), iterating until the reviewers give their thumbs up–which is signalled usually by a comment saying `LGTM`, which means "Looks Good To Me". +1. If the code change needs to be applied to other branches as well (for example a bugfix needing to be backported to a previous version), one of the team will either ask you to submit a PR with the same commit to the old branch, or do this for you. +1. Once everything is said and done, your pull request gets merged. You've made it! + +The TL;DR; of the above very precise workflow version is: + +1. Fork meteole +2. Hack and test on your feature (on a branch) +3. Document it +4. Submit a PR +6. Keep polishing it until received thumbs up from the core team +7. Profit! + +## External dependencies + +All the external runtime dependencies for the project, including transitive dependencies, must have an open source license that is equal to, or compatible with, [Apache 2](http://www.apache.org/licenses/LICENSE-2.0). + +This must be ensured by manually verifying the license for all the dependencies for the project: + +1. Whenever a committer to the project changes a version of a dependency (including Scala) in the build file. +2. Whenever a committer to the project adds a new dependency. +3. Whenever a new release is cut (public or private for a customer). + +Which licenses are compatible with Apache 2 are defined in [this doc](http://www.apache.org/legal/3party.html#category-a), where you can see that the licenses that are listed under ``Category A`` are automatically compatible with Apache 2, while the ones listed under ``Category B`` need additional action: + +> Each license in this category requires some degree of [reciprocity](http://www.apache.org/legal/3party.html#define-reciprocal); therefore, additional action must be taken in order to minimize the chance that a user of an Apache product will create a derivative work of a reciprocally-licensed portion of an Apache product without being aware of the applicable requirements. + +Each project must also create and maintain a list of all dependencies and their licenses, including all their transitive dependencies. This can be done either in the documentation or in the build file next to each dependency. + +You must add the dependency and its licence in https://github.com/MAIF/meteole/blob/master/licences.md + +## Documentation + +If you add features to *Meteole*, don't forget to modify the user documentation : + +* https://github.com/MAIF/meteole/tree/master/docs/ + +## Tests + +Every new feature should provide corresponding tests to ensure everything is working and will still working in future releases. To run the tests, just run + +```sh +pytest tests/ +``` + +## Continuous integration + +TODO diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/LICENCE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..cc98ba8 --- /dev/null +++ b/NOTICE @@ -0,0 +1,15 @@ +This software is licensed under the Apache 2 license, quoted below. + +Copyright 2024 MAIF and contributors + +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. diff --git a/README.md b/README.md index 3aad52a..12e8ee4 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ client = Vigilance(application_id=APPLICATION_ID) df_phenomenon, df_timelaps = client.get_phenomenon() # pour accéder aux phénomènes prévus -textes_vigilance = client.get_textes_vigilance() # pour accéder aux bulletins de vigilance +textes_vigilance = client.get_vigilance_bulletin() # pour accéder aux bulletins de vigilance client.get_vignette() # pour afficher les vignettes ``` @@ -87,3 +87,18 @@ client.get_vignette() # pour afficher les vignettes To have more documentation from MeteoFrance in Vigilance Bulletin : - [Meteo France Documentation](https://donneespubliques.meteofrance.fr/?fond=produit&id_produit=305&id_rubrique=50) + +## Contributing + +Contributions are *very* welcome! + +If you see an issue that you'd like to see fixed, the best way to make it happen is to help out by submitting a pull request implementing it. + +Refer to the [CONTRIBUTING.md](./CONTRIBUTING.md) file for more details about the workflow, +and general hints on how to prepare your pull request. You can also ask for clarifications or guidance in GitHub issues directly. + +## License + +This project is Open Source and available under the Apache 2 License. + +[![Alt MAIF Logo](https://static.maif.fr/resources/img/logo-maif.svg)](https://www.maif.fr/) diff --git a/docs/pages/how_to.md b/docs/pages/how_to.md index 27d1774..8b4ff52 100644 --- a/docs/pages/how_to.md +++ b/docs/pages/how_to.md @@ -34,13 +34,13 @@ For data usage, access the predicted phenomena to trigger modeling based on the ```python from meteole import Vigilance -# application_id: get it on Météo-France portal +# application_id: obtain it on the Météo-France API portal client = Vigilance(application_id=APPLICATION_ID) df_phenomenon, df_timelaps = client.get_phenomenon() # Fetch vigilance bulletins -textes_vigilance = client.get_textes_vigilance() +textes_vigilance = client.get_vigilance_bulletin() # Display the vigilance vignette client.get_vignette() diff --git a/licences.md b/licences.md new file mode 100644 index 0000000..2ef4771 --- /dev/null +++ b/licences.md @@ -0,0 +1,20 @@ +# Licenses for Project Dependencies + +This file lists the licenses for the Python libraries used in this project, following best practices for transparency and compliance. + +## Dependencies and Their Licenses + +| Library | Minimum Version | License | License Link | +|-------------------|-----------------|---------------------|-------------------------------------------------------| +| `pandas` | 2.0.0 | **BSD 3-Clause** | [View License](https://github.com/pandas-dev/pandas/blob/main/LICENSE) | +| `ecmwflibs` | 0.6.3 | **Apache 2.0** | [View License](https://github.com/ecmwf/ecmwflibs/blob/main/LICENSE) | +| `cfgrib` | 0.0.11.0 | **Apache 2.0** | [View License](https://github.com/ecmwf/cfgrib/blob/main/LICENSE) | +| `requests` | 2.31.0 | **Apache 2.0** | [View License](https://github.com/psf/requests/blob/main/LICENSE) | +| `xarray` | 2024.5.0 | **Apache 2.0** | [View License](https://github.com/pydata/xarray/blob/main/LICENSE) | +| `xmltodict` | 0.13.0 | **MIT License** | [View License](https://github.com/martinblech/xmltodict/blob/master/LICENSE) | +| `matplotlib` | 3.8.4 | **PSF License** | [View License](https://github.com/matplotlib/matplotlib/blob/main/LICENSE/LICENSE) | + +## Notes + +- The licenses listed above correspond to the official repositories of each library as of their respective versions. +- It is recommended to verify the licenses when updating dependencies to ensure ongoing compliance. diff --git a/src/meteole/__init__.py b/src/meteole/__init__.py index beaa6e6..3d093e8 100644 --- a/src/meteole/__init__.py +++ b/src/meteole/__init__.py @@ -15,16 +15,13 @@ def setup_logger(): logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) - # Créer un gestionnaire de flux (StreamHandler) pour afficher les logs dans la console handler = logging.StreamHandler() handler.setLevel(logging.INFO) - # Créer un formatteur et l'ajouter au gestionnaire # formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') formatter = logging.Formatter("%(message)s") handler.setFormatter(formatter) - # Ajouter le gestionnaire au logger return logger.addHandler(handler) diff --git a/src/meteole/_arome.py b/src/meteole/_arome.py index 01e3d28..1aba4a7 100644 --- a/src/meteole/_arome.py +++ b/src/meteole/_arome.py @@ -46,7 +46,7 @@ class AromeForecast(forecast.Forecast): - """Access the AROME numerical Forecast.""" + """Access the AROME numerical forecast data.""" api_version = "1.0" base_url = const.API_BASE_URL + "arome/" + api_version @@ -61,30 +61,22 @@ def __init__( cache_dir: str | None = None, ): """ - Init the AromeForecast object. - - Parameters - ---------- - precision : {0.01, 0.025}, optional - the resolution of the AROME Model, by default 0.01 - territory : str, optional - The AROME territory to fetch, by default "FRANCE" - api_key : str | None, optional - The API Key, by default None - token : str | None, optional - The API Token, by default None - application_id : str | None, optional - The Application ID, by default None - cache_dir : str | None, optional - The path to the caching directory, by default None. - If None, the cache directory is set to "/tmp/cache". - - Note - ---- - See :class:`.MeteoFranceClient` for the parameters `api_key`, `token` and `application_id`. - - The available territories are listed in :data:`.AVAILABLE_TERRITORY`. - + Initializes an AromeForecast object for accessing AROME forecast data. + + Args: + api_key (str | None, optional): The API key for authentication. Defaults to None. + territory (str, optional): The AROME territory to fetch. Defaults to "FRANCE". + precision (float, optional): The resolution of the AROME model. Supported values are + `0.01` (high resolution) and `0.025` (lower resolution). Defaults to 0.01. + token (str | None, optional): The API token for authentication. Defaults to None. + application_id (str | None, optional): The application ID for authentication. Defaults to None. + cache_dir (str | None, optional): The path to the caching directory. Defaults to None. + If not provided, the cache directory is set to "/tmp/cache". + + Notes: + - See `MeteoFranceClient` for additional details on the parameters `api_key`, `token`, + and `application_id`. + - Available territories are listed in the `AVAILABLE_TERRITORY` constant. """ super().__init__(api_key, token, territory, precision, application_id, cache_dir) diff --git a/src/meteole/_arpege.py b/src/meteole/_arpege.py index fb2eeb8..16c2a61 100644 --- a/src/meteole/_arpege.py +++ b/src/meteole/_arpege.py @@ -64,7 +64,7 @@ class ArpegeForecast(forecast.Forecast): - """Access the ARPEGE numerical Forecast.""" + """Access the ARPEGE numerical forecast data.""" api_version = "1.0" base_url = const.API_BASE_URL + "arpege/" + api_version @@ -78,28 +78,22 @@ def __init__( cache_dir: str | None = None, ): """ - Init the ArpegeForecast object. - Note that `precision` is infered from `territory`. - - Parameters - ---------- - api_key : str | None, optional - The API Key, by default None - token : str | None, optional - The API Token, by default None - territory : str, optional - The ARPEGE territory to fetch, by default "FRANCE" - application_id : str | None, optional - The Application ID, by default None - cache_dir : str | None, optional - The path to the caching directory, by default None. - If None, the cache directory is set to "/tmp/cache". - - Note - ---- - See :class:`.MeteoFranceClient` for the parameters `api_key`, `token` and `application_id`. - - The available territories are listed in :data:`.AVAILABLE_TERRITORY`. + Initializes an ArpegeForecast object for accessing ARPEGE forecast data. + + The `precision` of the forecast is inferred from the specified `territory`. + + Args: + territory (str, optional): The ARPEGE territory to fetch. Defaults to "EUROPE". + api_key (str | None, optional): The API key for authentication. Defaults to None. + token (str | None, optional): The API token for authentication. Defaults to None. + application_id (str | None, optional): The Application ID for authentication. Defaults to None. + cache_dir (str | None, optional): Path to the cache directory. Defaults to None. + If not provided, the cache directory is set to "/tmp/cache". + + Notes: + - See `MeteoFranceClient` for additional details on the parameters `api_key`, `token`, + and `application_id`. + - Available territories are listed in the `AVAILABLE_TERRITORY` constant. """ super().__init__( diff --git a/src/meteole/_vigilance.py b/src/meteole/_vigilance.py index b6faf9e..d5e3d33 100644 --- a/src/meteole/_vigilance.py +++ b/src/meteole/_vigilance.py @@ -35,29 +35,26 @@ def __init__( application_id: str | None = None, ): """ - Init the Vigilance object. - Parameters - ---------- - api_key : str | None, optional - The API Key, by default None - token : str | None, optional - The API Token, by default None - application_id : str | None, optional - The Application ID, by default None - Note - ---- - See :class:`.MeteoFranceClient` for the parameters `api_key`, `token` and `application_id`. + Initializes the Vigilance object. + + Args: + api_key (str | None, optional): The API key for authentication. Defaults to None. + token (str | None, optional): The API token for authentication. Defaults to None. + application_id (str | None, optional): The application ID for authentication. Defaults to None. + + Notes: + See `MeteoFranceClient` for additional details on the parameters `api_key`, `token`, + and `application_id`. """ super().__init__(api_key, token, application_id) - def get_textes_vigilance(self) -> dict: + def get_vigilance_bulletin(self) -> dict: """ - Get bulletin de vigilance + Retrieve the vigilance bulletin. Returns: - -------- - dict: a Dict with bulletin de vigilance + dict: a Dict representing the vigilance bulletin """ url = self.base_url + self.version + "/textesvigilance/encours" @@ -67,7 +64,7 @@ def get_textes_vigilance(self) -> dict: return req.json() except MissingDataError as e: if "no matching blob" in e.message: - logger.warning("La vigilance en cours ne nécessite pas de publication") + logger.warning("Ongoing vigilance requires no publication") else: logger.error(f"Unexpected error: {e}") return {} @@ -75,13 +72,12 @@ def get_textes_vigilance(self) -> dict: logger.error(f"Unexpected error: {e}") return {} - def get_carte_vigilance(self) -> dict: + def get_vigilance_map(self) -> dict: """ - Get "carte" de Vigilance with risk prediction + Get the vigilance map with predicted risk displayed. Returns: - -------- - dict: a Dict with risk prediction + dict: a Dict with the predicted risk. """ url = self.base_url + self.version + "/cartevigilance/encours" logger.debug(f"GET {url}") @@ -91,13 +87,13 @@ def get_carte_vigilance(self) -> dict: def get_phenomenon(self) -> tuple[pd.DataFrame, pd.DataFrame]: """ - get risk prediction by phenomenon and by domain + Get risk prediction by phenomenon and by domain. + Returns: - -------- - pd.DataFrame: a DataFrame with phenomenon by id - pd.DataFrame: a DataFrame with phenomenon by domain + pd.DataFrame: a DataFrame with phenomenon by id + pd.DataFrame: a DataFrame with phenomenon by domain """ - df_carte = pd.DataFrame(self.get_carte_vigilance()) + df_carte = pd.DataFrame(self.get_vigilance_map()) periods_data = df_carte.loc["periods", "product"] df_periods = pd.json_normalize(periods_data) @@ -123,7 +119,7 @@ def get_phenomenon(self) -> tuple[pd.DataFrame, pd.DataFrame]: def get_vignette(self) -> None: """ - Get png + Get png. """ url = self.base_url + self.version + "/vignettenationale-J-et-J1/encours" diff --git a/src/meteole/client.py b/src/meteole/client.py index e1d88c1..74337f1 100644 --- a/src/meteole/client.py +++ b/src/meteole/client.py @@ -103,7 +103,11 @@ def get_token(self) -> str | None: params: dict[str, str] = {"grant_type": "client_credentials"} header: dict[str, str] = {"Authorization": "Basic " + str(self.application_id)} res: requests.Response = requests.post( - token_entrypoint, params=params, headers=header, timeout=(30, 3600), verify=str(self.verify) + token_entrypoint, + params=params, + headers=header, + timeout=(30, 3600), + verify=str(self.verify) if self.verify else None, ) self.token = res.json()["access_token"] @@ -116,21 +120,17 @@ def get_token(self) -> str | None: return self.token def _get_request(self, url, params=None, max_retries=5): - """Make a get request to the API. + """ + Makes a GET request to the API with optional retries. - Parameters - ---------- - url : str - the url to request - params : dict - the parameters to pass to the request - max_retries : int - the maximum number of retries + Args: + url (str): The URL to send the GET request to. + params (dict, optional): The query parameters to include in the request. Defaults to None. + max_retries (int, optional): The maximum number of retry attempts in case of failure. Defaults to 5. + + Returns: + requests.Response: The response returned by the API. - Returns - ------- - requests.Response - the response of the request """ logger.debug(f"GET {url}") attempt = 0 diff --git a/src/meteole/const.py b/src/meteole/const.py index f63208a..59b4049 100644 --- a/src/meteole/const.py +++ b/src/meteole/const.py @@ -18,13 +18,13 @@ FRANCE_METRO_LATITUDES = (41.33356, 51.0889) dict_phenomenon_id = { - "1": "vent", - "2": "pluie", - "3": "orages", - "4": "crues", - "5": "neige / verglas", - "6": "canicule", - "7": "grand froid", + "1": "wind", + "2": "rain", + "3": "storm", + "4": "flood", + "5": "snow_ice", + "6": "heat_wave", + "7": "extreme_cold", "8": "avalanches", - "9": "vagues submersion", + "9": "waves_submergence", } diff --git a/src/meteole/errors.py b/src/meteole/errors.py index 897b230..536b882 100644 --- a/src/meteole/errors.py +++ b/src/meteole/errors.py @@ -4,23 +4,18 @@ class GenericMeteofranceApiError(Exception): """Exception raised errors in the input parameters where a required field is missing. - Parameters - ---------- - message : str - Human-readable string descipting the exceptetion. - description : str - More detailed description of the error.""" + Args: + message (str): Human-readable string descipting the exceptetion. + description (str): More detailed description of the error.""" def init(self, text: str): """Initialize the exception with an error message parsed from an XML string. - Parameters - ---------- - text str: - XML string containing the error details, - expected to follow a specific schema with 'am:fault' as the root - element and 'am:message' and 'am:description' as child elements.""" + Args: + text (str): XML string containing the error details, + expected to follow a specific schema with 'am:fault' as the root + element and 'am:message' and 'am:description' as child elements.""" # parse the error message with xmltodict data = xmltodict.parse(text) @@ -37,12 +32,10 @@ def init(self, text: str): """Initialize the exception with an error message parsed from an XML string. - Parameters - ---------- - text str: - XML string containing the error details, - expected to follow a specific schema with 'am:fault' as the root - element and 'am:message' and 'am:description' as child elements.""" + Args: + text (str): XML string containing the error details, + expected to follow a specific schema with 'am:fault' as the root + element and 'am:message' and 'am:description' as child elements.""" # parse the error message with xmltodict try: @@ -50,8 +43,8 @@ def init(self, text: str): exception = data["mw:fault"]["mw:description"]["ns0:ExceptionReport"]["ns0:Exception"] code = exception["@exceptionCode"] locator = exception["@locator"] - text = exception["ns0:ExceptionText"] - message = f"Error code: {code}\nLocator: {locator}\nText: {text}" + exception_text = exception["ns0:ExceptionText"] + message = f"Error code: {code}\nLocator: {locator}\nText: {exception_text}" except Exception: message = text self.message = message diff --git a/src/meteole/forecast.py b/src/meteole/forecast.py index a3674ee..0952bbf 100644 --- a/src/meteole/forecast.py +++ b/src/meteole/forecast.py @@ -162,19 +162,22 @@ def _get_coverage_id( interval: str | None = None, ) -> str: """ - Get a coverage_id from `capabilities`. + Retrieves a `coverage_id` from the capabilities based on the provided parameters. - Parameters: - indicator (str): required. - run (Optional[str]): Identifies the model inference. Defaults to latest if None. Format "YYYY-MM-DDTHH:MM:SSZ". - interval (Optional[str]): aggregation period. Must be None for instant indicators, otherwise raises. Defaults to P1D for time-aggregated indicators like TOTAL_PRECIPITATION. + Args: + indicator (str): The indicator to retrieve. This parameter is required. + run (str | None, optional): The model inference timestamp. If None, defaults to the latest available run. + Expected format: "YYYY-MM-DDTHH:MM:SSZ". Defaults to None. + interval (str | None, optional): The aggregation period. Must be None for instant indicators; + raises an error if specified. Defaults to "P1D" for time-aggregated indicators such as + TOTAL_PRECIPITATION. Returns: - str: coverage_id + str: The `coverage_id` corresponding to the given parameters. Raises: - ValueError: If no or invalid 'indicator' - ValueError: If invalid interval, or if missing interval when required + ValueError: If `indicator` is missing or invalid. + ValueError: If `interval` is invalid or required but missing. """ if not hasattr(self, "capabilities"): self.get_capabilities() @@ -287,7 +290,8 @@ def get_coverage( def _raise_if_invalid_or_fetch_default( self, param_name: str, inputs: list[int] | None, availables: list[int] ) -> list[int]: - """ + """Checks validity of `inputs`. + Checks if the elements in `inputs` are in `availables` and raises a ValueError if not. If `inputs` is empty or None, uses the first element from `availables` as the default value. @@ -384,16 +388,12 @@ def _get_coverage_description(self, coverage_id: str) -> dict: In the future, it should be possible to use it to get the available heights, times, latitudes and longitudes of the forecast. - Parameters - ---------- - coverage_id: str - the Coverage ID. Use :meth:`get_coverage` to access the available coverage ids. - By default use the latest temperature coverage ID. + Args: + coverage_id (str): the Coverage ID. Use :meth:`get_coverage` to access the available coverage ids. + By default use the latest temperature coverage ID. - Returns - ------- - description : dict - the description of the coverage. + Returns: + description (dict): the description of the coverage. """ url = f"{self.base_url}/{self.entry_point}/DescribeCoverage" params = { @@ -477,37 +477,37 @@ def _get_coverage_file( file_format: str = "grib", filepath: Path | None = None, ) -> Path: - """Fetch the raster values of the model predictions. - - The raster is saved to a file in the cache directory. - - Parameters - ---------- - coverage_id: str - The ID of the coverage to fetch. Use the `get_coverage` method to find available coverage IDs. - By default, it uses the latest temperature coverage ID. - height: int, optional - The height in meters for the model. Defaults to 2 meters above ground. - The available heights can be accessed from the API, but this feature is not implemented yet. - forecast_horizon_in_seconds: int, optional - The forecast horizon in seconds into the future. Defaults to 0s (current time). - The available forecast horizons can be known via :meth:`get_coverage_description` - lat: tuple[float], optional - The minimum and maximum latitudes to return. Defaults to the latitudes of France. - long: tuple[float], optional - The minimum and maximum longitudes to return. Defaults to the longitudes of France. - file_format: str, optional - The format of the file to save the raster data. Defaults to "grib". - filepath: Path, optional - The path where the file will be saved. If not provided, it will be saved in the cache directory. - - Returns - ------- - filename : pathlib.Path - The path to the file containing the raster data in the specified format. - - .. see-also:: - :func:`.raster.plot_tiff_file` to plot the file. + """ + Retrieves raster data for a specified model prediction and saves it to a file. + + If no `filepath` is provided, the file is saved to a default cache directory under + the current working directory. + + Args: + coverage_id (str): The coverage ID to retrieve. Use `get_coverage` to list available coverage IDs. + height (int, optional): The height above ground level in meters. Defaults to 2 meters. + If not provided, no height subset is applied. + pressure (int, optional): The pressure level in hPa. If not provided, no pressure subset is applied. + forecast_horizon_in_seconds (int, optional): The forecast horizon in seconds into the future. + Defaults to 0 (current time). + lat (tuple[float, float], optional): Tuple specifying the minimum and maximum latitudes. + Defaults to (37.5, 55.4), covering the latitudes of France. + long (tuple[float, float], optional): Tuple specifying the minimum and maximum longitudes. + Defaults to (-12, 16), covering the longitudes of France. + file_format (str, optional): The format of the raster file. Supported formats are "grib" and "tiff". + Defaults to "grib". + filepath (Path, optional): The file path where the raster file will be saved. If not specified, + the file is saved to a cache directory. + + Returns: + Path: The file path to the saved raster data. + + Notes: + - If the file does not exist in the cache, it will be fetched from the API and saved. + - Supported subsets include pressure, height, time, latitude, and longitude. + + See Also: + raster.plot_tiff_file: Method for plotting raster data stored in TIFF format. """ self.filepath = filepath diff --git a/tests/test_vigilance.py b/tests/test_vigilance.py index 959cd44..b7b5b49 100644 --- a/tests/test_vigilance.py +++ b/tests/test_vigilance.py @@ -14,32 +14,32 @@ def setUp(self): self.vigilance = Vigilance(api_key=self.api_key, token=self.token, application_id=self.application_id) @patch("meteole._vigilance.Vigilance._get_request") - def test_get_textes_vigilance(self, mock_get_request): + def test_get_vigilance_bulletin(self, mock_get_request): mock_response = MagicMock() mock_response.json.return_value = {"data": "some_data"} mock_get_request.return_value = mock_response - result = self.vigilance.get_textes_vigilance() + result = self.vigilance.get_vigilance_bulletin() self.assertEqual(result, {"data": "some_data"}) mock_get_request.assert_called_once_with( "https://public-api.meteofrance.fr/public/DPVigilance/v1/textesvigilance/encours" ) @patch("meteole._vigilance.Vigilance._get_request") - def test_get_carte_vigilance(self, mock_get_request): + def test_get_vigilance_map(self, mock_get_request): mock_response = MagicMock() mock_response.json.return_value = {"data": "some_data"} mock_get_request.return_value = mock_response - result = self.vigilance.get_carte_vigilance() + result = self.vigilance.get_vigilance_map() self.assertEqual(result, {"data": "some_data"}) mock_get_request.assert_called_once_with( "https://public-api.meteofrance.fr/public/DPVigilance/v1/cartevigilance/encours" ) - @patch("meteole._vigilance.Vigilance.get_carte_vigilance") - def test_get_phenomenon(self, mock_get_carte_vigilance): - mock_get_carte_vigilance.return_value = { + @patch("meteole._vigilance.Vigilance.get_vigilance_map") + def test_get_phenomenon(self, mock_get_vigilance_map): + mock_get_vigilance_map.return_value = { "product": { "periods": [ { @@ -68,7 +68,7 @@ def test_get_phenomenon(self, mock_get_carte_vigilance): } } - with patch.object(self.vigilance, "get_carte_vigilance", return_value=mock_get_carte_vigilance.return_value): + with patch.object(self.vigilance, "get_vigilance_map", return_value=mock_get_vigilance_map.return_value): df_phenomenon, df_timelaps = self.vigilance.get_phenomenon() expected_phenomenon_data = { @@ -79,7 +79,7 @@ def test_get_phenomenon(self, mock_get_carte_vigilance): [{"color_id": 2, "color_name": "Jaune", "count": 3}], ], "echeance": ["J", "J1"], - "phenomenon_libelle": ["vent", "pluie"], + "phenomenon_libelle": ["wind", "rain"], } expected_phenomenon_df = pd.DataFrame(expected_phenomenon_data) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..6f00f5e --- /dev/null +++ b/tox.ini @@ -0,0 +1,11 @@ +[tox] +min_version = 4.0 +env_list = + py311 + py310 +[testenv] +description = run unit tests +deps = + pytest + pytest-cov +commands = pytest tests --cov --cov-append diff --git a/tutorial/access_vigilance_bulletin.ipynb b/tutorial/access_vigilance_bulletin.ipynb new file mode 100644 index 0000000..990eb5b --- /dev/null +++ b/tutorial/access_vigilance_bulletin.ipynb @@ -0,0 +1,217 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7fb27b941602401d91542211134fc71a", + "metadata": {}, + "source": [ + "# Vigilance Bulletin\n", + "\n", + "This tutorial will help you access the vigilance bulletin\n", + "\n", + "For more documentation, click [here](https://donneespubliques.meteofrance.fr/?fond=produit&id_produit=305&id_rubrique=50).\n", + "\n", + "Contents:\n", + "\n", + "- Init Vigilance Class\n", + "- Access Data" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "acae54e37e7d407bbb7b55eff062a284", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "import pandas as pd\n", + "\n", + "from meteole import Vigilance" + ] + }, + { + "cell_type": "markdown", + "id": "9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": {}, + "source": [ + "# Init Vigilance Class" + ] + }, + { + "cell_type": "markdown", + "id": "8dd0d8092fe74a7c96281538738b07e2", + "metadata": {}, + "source": [ + "**Requirements notice** : TODO Link to the documentation to have application_id" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "72eea5119410473aa328ad9291626812", + "metadata": {}, + "outputs": [], + "source": [ + "# load application_id from .env\n", + "from dotenv import load_dotenv\n", + "\n", + "load_dotenv()\n", + "application_id = os.getenv(\"APPLICATION_ID\", None)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "8edb47106e1a46a883d545849b8ab81b", + "metadata": {}, + "outputs": [], + "source": [ + "client = Vigilance(application_id=application_id)" + ] + }, + { + "cell_type": "markdown", + "id": "10185d26023b46108eb7d9f57d49d2b3", + "metadata": {}, + "source": [ + "# Collect Forecasted phenomenon\n", + "\n", + "Collect vigilance data from M\u00e9t\u00e9o France, including the forecasted phenomenon in df_phenomenon and the maximum intensity for each zone in df_timelaps" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "8763a12b2bbd4a93a75aff182afb95dc", + "metadata": {}, + "outputs": [], + "source": [ + "df_phenomenon, df_timelaps = client.get_phenomenon()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7623eae2785240b9bd12b16a66d81610", + "metadata": {}, + "outputs": [], + "source": [ + "df_phenomenon.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7cdc8c89c7104fffa095e18ddfef8986", + "metadata": {}, + "outputs": [], + "source": [ + "df_timelaps.head()" + ] + }, + { + "cell_type": "markdown", + "id": "b118ea5561624da68c537baed56e602f", + "metadata": {}, + "source": [ + "# Collect text of monitoring bulletins\n", + "\n", + "Contains the text of monitoring bulletins, whether national, zonal (in the sense of defense zones) or departmental. It is issued in addition to the Vigilance card, when the meteorological situation so requires (systematically in Vigilance Orange and Red, when necessary in Vigilance Yellow)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "938c804e27f84196a10c8828c723f798", + "metadata": {}, + "outputs": [], + "source": [ + "vigilance_bulletin = client.get_vigilance_bulletin()" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "504fb2a444614c0babb325280ed9130a", + "metadata": {}, + "outputs": [], + "source": [ + "vigilance_bulletin_df = pd.json_normalize(vigilance_bulletin)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b43b363d81ae4b689946ece5c682cd59", + "metadata": {}, + "outputs": [], + "source": [ + "text_bloc_items_df = pd.json_normalize(vigilance_bulletin_df[\"product.text_bloc_items\"].explode())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a65eabff63a45729fe45fb5ade58bdc", + "metadata": {}, + "outputs": [], + "source": [ + "text_bloc_items_df.head(10)" + ] + }, + { + "cell_type": "markdown", + "id": "c3933fab20d04ec698c2621248eb3be0", + "metadata": {}, + "source": [ + "# Map display" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4dd4641cc4064e0191573fe9c69df29b", + "metadata": {}, + "outputs": [], + "source": [ + "client.get_vignette()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "meteole_env", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "undefined.undefined.undefined" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tutorial/access_viligance_bulletin.ipynb b/tutorial/access_viligance_bulletin.ipynb deleted file mode 100644 index ee9803d..0000000 --- a/tutorial/access_viligance_bulletin.ipynb +++ /dev/null @@ -1,415 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "7fb27b941602401d91542211134fc71a", - "metadata": {}, - "source": [ - "# Vigilance Bulletin\n", - "\n", - "This tutorial will help you access the vigilance bulletin\n", - "\n", - "For more documentation, click [here](https://donneespubliques.meteofrance.fr/?fond=produit&id_produit=305&id_rubrique=50).\n", - "\n", - "Contents:\n", - "\n", - "- Init Vigilance Class\n", - "- Access Data" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "acae54e37e7d407bbb7b55eff062a284", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "\n", - "import pandas as pd\n", - "\n", - "from meteole import Vigilance" - ] - }, - { - "cell_type": "markdown", - "id": "9a63283cbaf04dbcab1f6479b197f3a8", - "metadata": {}, - "source": [ - "# Init Vigilance Class" - ] - }, - { - "cell_type": "markdown", - "id": "8dd0d8092fe74a7c96281538738b07e2", - "metadata": {}, - "source": [ - "**Requirements notice** : TODO Link to the documentation to have application_id" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "72eea5119410473aa328ad9291626812", - "metadata": {}, - "outputs": [], - "source": [ - "# load application_id from .env\n", - "from dotenv import load_dotenv\n", - "\n", - "load_dotenv()\n", - "application_id = os.getenv(\"APPLICATION_ID\", None)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "8edb47106e1a46a883d545849b8ab81b", - "metadata": {}, - "outputs": [], - "source": [ - "client = Vigilance(application_id=application_id)" - ] - }, - { - "cell_type": "markdown", - "id": "10185d26023b46108eb7d9f57d49d2b3", - "metadata": {}, - "source": [ - "# Collect Forecasted phenomenon\n", - "\n", - "Collect vigilance data from M\u00e9t\u00e9o France, including the forecasted phenomenon in df_phenomenon and the maximum intensity for each zone in df_timelaps" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "8763a12b2bbd4a93a75aff182afb95dc", - "metadata": {}, - "outputs": [], - "source": [ - "df_phenomenon, df_timelaps = client.get_phenomenon()" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "7623eae2785240b9bd12b16a66d81610", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
phenomenon_idany_color_countphenomenon_countsecheancephenomenon_libelle
042[{'color_id': 2, 'color_name': 'Jaune', 'count...Jcrues
185[{'color_id': 2, 'color_name': 'Jaune', 'count...Javalanches
215[{'color_id': 2, 'color_name': 'Jaune', 'count...J1vent
342[{'color_id': 2, 'color_name': 'Jaune', 'count...J1crues
492[{'color_id': 2, 'color_name': 'Jaune', 'count...J1vagues submersion
\n", - "
" - ], - "text/plain": [ - " phenomenon_id any_color_count \\\n", - "0 4 2 \n", - "1 8 5 \n", - "2 1 5 \n", - "3 4 2 \n", - "4 9 2 \n", - "\n", - " phenomenon_counts echeance \\\n", - "0 [{'color_id': 2, 'color_name': 'Jaune', 'count... J \n", - "1 [{'color_id': 2, 'color_name': 'Jaune', 'count... J \n", - "2 [{'color_id': 2, 'color_name': 'Jaune', 'count... J1 \n", - "3 [{'color_id': 2, 'color_name': 'Jaune', 'count... J1 \n", - "4 [{'color_id': 2, 'color_name': 'Jaune', 'count... J1 \n", - "\n", - " phenomenon_libelle \n", - "0 crues \n", - "1 avalanches \n", - "2 vent \n", - "3 crues \n", - "4 vagues submersion " - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df_phenomenon.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "7cdc8c89c7104fffa095e18ddfef8986", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
domain_idmax_color_idphenomenon_itemsecheance
0101[{'phenomenon_id': '1', 'phenomenon_max_color_...J
1111[{'phenomenon_id': '1', 'phenomenon_max_color_...J
2121[{'phenomenon_id': '1', 'phenomenon_max_color_...J
3131[{'phenomenon_id': '1', 'phenomenon_max_color_...J
4141[{'phenomenon_id': '1', 'phenomenon_max_color_...J
\n", - "
" - ], - "text/plain": [ - " domain_id max_color_id phenomenon_items \\\n", - "0 10 1 [{'phenomenon_id': '1', 'phenomenon_max_color_... \n", - "1 11 1 [{'phenomenon_id': '1', 'phenomenon_max_color_... \n", - "2 12 1 [{'phenomenon_id': '1', 'phenomenon_max_color_... \n", - "3 13 1 [{'phenomenon_id': '1', 'phenomenon_max_color_... \n", - "4 14 1 [{'phenomenon_id': '1', 'phenomenon_max_color_... \n", - "\n", - " echeance \n", - "0 J \n", - "1 J \n", - "2 J \n", - "3 J \n", - "4 J " - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df_timelaps.head()" - ] - }, - { - "cell_type": "markdown", - "id": "b118ea5561624da68c537baed56e602f", - "metadata": {}, - "source": [ - "# Collect text of monitoring bulletins\n", - "\n", - "Contains the text of monitoring bulletins, whether national, zonal (in the sense of defense zones) or departmental. It is issued in addition to the Vigilance card, when the meteorological situation so requires (systematically in Vigilance Orange and Red, when necessary in Vigilance Yellow)." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "938c804e27f84196a10c8828c723f798", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "missing data\n", - "Unexpected error: {\"detail\":\"no matching blob\"}\n" - ] - } - ], - "source": [ - "textes_vigilance = client.get_textes_vigilance()" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "504fb2a444614c0babb325280ed9130a", - "metadata": {}, - "outputs": [], - "source": [ - "test_texte = pd.json_normalize(textes_vigilance)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b43b363d81ae4b689946ece5c682cd59", - "metadata": {}, - "outputs": [], - "source": [ - "df_text_bloc_items = pd.json_normalize(test_texte[\"product.text_bloc_items\"].explode())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8a65eabff63a45729fe45fb5ade58bdc", - "metadata": {}, - "outputs": [], - "source": [ - "df_text_bloc_items.head(10)" - ] - }, - { - "cell_type": "markdown", - "id": "c3933fab20d04ec698c2621248eb3be0", - "metadata": {}, - "source": [ - "# Map display" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4dd4641cc4064e0191573fe9c69df29b", - "metadata": {}, - "outputs": [], - "source": [ - "client.get_vignette()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "meteole_env", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.11" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/tutorial/arome.ipynb b/tutorial/arome.ipynb index 81ca21e..96a9901 100644 --- a/tutorial/arome.ipynb +++ b/tutorial/arome.ipynb @@ -27,23 +27,9 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Fetching all available coverages...\n", - "\n", - "\t Successfully fetched 2020 coverages,\n", - "\t representing 19 different indicators,\n", - "\t across the last 37 runs (from 2024-12-08T00.00.00Z to 2024-12-12T12.00.00Z).\n", - "\n", - "\t Default run for `get_coverage`: 2024-12-12T12.00.00Z)\n" - ] - } - ], + "outputs": [], "source": [ "# init client\n", "arome = AromeForecast(application_id=application_id)" @@ -51,17 +37,9 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Indicator: TOTAL_PRECIPITATION_RATE__GROUND_OR_WATER_SURFACE\n" - ] - } - ], + "outputs": [], "source": [ "# pick a random indicator\n", "\n", @@ -71,161 +49,9 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Using latest `run=2024-12-08T00.00.00Z`.\n", - "Using `forecast_horizons=[1]`\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
latitudelongituderunforecast_horizonunknown
051.08-5.142024-12-080 days 01:00:000.195312
151.08-5.132024-12-080 days 01:00:000.162109
251.08-5.122024-12-080 days 01:00:000.101562
351.08-5.112024-12-080 days 01:00:000.052734
451.08-5.102024-12-080 days 01:00:000.025391
..................
143422041.349.522024-12-080 days 01:00:000.000000
143422141.349.532024-12-080 days 01:00:000.000000
143422241.349.542024-12-080 days 01:00:000.000000
143422341.349.552024-12-080 days 01:00:000.000000
143422441.349.562024-12-080 days 01:00:000.000000
\n", - "

1434225 rows \u00d7 5 columns

\n", - "
" - ], - "text/plain": [ - " latitude longitude run forecast_horizon unknown\n", - "0 51.08 -5.14 2024-12-08 0 days 01:00:00 0.195312\n", - "1 51.08 -5.13 2024-12-08 0 days 01:00:00 0.162109\n", - "2 51.08 -5.12 2024-12-08 0 days 01:00:00 0.101562\n", - "3 51.08 -5.11 2024-12-08 0 days 01:00:00 0.052734\n", - "4 51.08 -5.10 2024-12-08 0 days 01:00:00 0.025391\n", - "... ... ... ... ... ...\n", - "1434220 41.34 9.52 2024-12-08 0 days 01:00:00 0.000000\n", - "1434221 41.34 9.53 2024-12-08 0 days 01:00:00 0.000000\n", - "1434222 41.34 9.54 2024-12-08 0 days 01:00:00 0.000000\n", - "1434223 41.34 9.55 2024-12-08 0 days 01:00:00 0.000000\n", - "1434224 41.34 9.56 2024-12-08 0 days 01:00:00 0.000000\n", - "\n", - "[1434225 rows x 5 columns]" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# fetch data using default computed params\n", "arome.get_coverage(random_indicator)" @@ -248,7 +74,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.11" + "version": "undefined.undefined.undefined" }, "toc": { "base_numbering": 1,