-
Notifications
You must be signed in to change notification settings - Fork 0
Add invalidate study script #60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a281595
6ce72d2
ead6220
f7b6f47
4708639
fea281d
3d51909
40c4b05
6c0c1bf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| # | ||
| # Copyright (c) 2026, RTE (http://www.rte-france.com) | ||
| # This Source Code Form is subject to the terms of the Mozilla Public | ||
| # License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| # file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
| # | ||
|
|
||
| import sys | ||
| import requests | ||
| import constant | ||
| from functions.studies.studies import invalidate_study | ||
| from tqdm import tqdm | ||
|
|
||
| # | ||
| # Invalidates built nodes and delete initial variant network for all studies that have not been modified since a given duration. | ||
| # | ||
| # Usage: | ||
| # python invalidate_unmodified_studies.py <duration> [--dry-run] [--limit <n>] | ||
| # | ||
| # Arguments: | ||
| # duration ISO 8601 duration (e.g. P365D for 1 year, P30D for 30 days, PT24H for 24 hours) | ||
| # --dry-run Optional flag to only list affected studies without performing any invalidation | ||
| # --limit <n> Optional maximum number of studies to process | ||
| # | ||
| # Example: | ||
| # python invalidate_unmodified_studies.py P365D --dry-run | ||
| # python invalidate_unmodified_studies.py P365D --limit 10 --dry-run | ||
| # | ||
|
|
||
| # | ||
| # @author Hugo Marcellin <hugo.marcellin_externe at rte-france.com> | ||
| # | ||
|
|
||
| def get_unmodified_studies(duration): | ||
| response = requests.get(constant.GET_UNMODIFIED_DIRECTORY_ELEMENTS, params={"elementType": "STUDY", "duration": duration}) | ||
| response.raise_for_status() | ||
| return response.json() | ||
|
|
||
| def invalidate_unmodified_studies(duration, dry_run=False, limit=None): | ||
| if constant.DEV: | ||
| print(f"\nDEV={str(constant.DEV)} -> hostnames configured for a local execution (172.17.0.1:xxxx)") | ||
|
|
||
| print(f"Fetching studies not modified since {duration}...") | ||
| studies = get_unmodified_studies(duration) | ||
|
|
||
| if not studies: | ||
| print("No unmodified studies found.") | ||
| return | ||
|
|
||
| print(f"Found {len(studies)} unmodified study/studies.") | ||
|
|
||
| if limit is not None and limit < len(studies): | ||
| print(f"Limit applied: processing {limit} out of {len(studies)} studies.") | ||
| studies = studies[:limit] | ||
|
|
||
| print("Selected studies:") | ||
| for study in studies: | ||
| print(f" - {study['elementUuid']} | {study['elementName']} | last modified: {study['lastModificationDate']}") | ||
|
|
||
| if dry_run: | ||
| print("\nDry run mode: no study will be invalidated.") | ||
| return | ||
|
flomillot marked this conversation as resolved.
|
||
|
|
||
| print("\nUnmounting studies...") | ||
| success_count = 0 | ||
| failure_count = 0 | ||
| for study in tqdm(studies): | ||
| try: | ||
| study_uuid = study["elementUuid"] | ||
| result = invalidate_study(study_uuid) | ||
| result.raise_for_status() | ||
| success_count += 1 | ||
| except Exception as e: | ||
| failure_count += 1 | ||
| tqdm.write(f" FAILED - {study_uuid} (error: {str(e)})") | ||
| if isinstance(e, requests.exceptions.RequestException) and e.response is not None: | ||
|
Comment on lines
+68
to
+76
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: cat -n scripts/invalidate_unmodified_studies.py | sed -n '60,85p'Repository: gridsuite/admin-tools Length of output: 1312 Fix unsafe exception handling: If line 69 raises an exception (e.g., missing "elementUuid" key), Proposed fix- for study in tqdm(studies):
- try:
- study_uuid = study["elementUuid"]
+ for study in tqdm(studies):
+ study_uuid = study.get("elementUuid", "<missing-elementUuid>")
+ try:
+ if study_uuid == "<missing-elementUuid>":
+ raise KeyError("elementUuid")
result = invalidate_study(study_uuid)
result.raise_for_status()
success_count += 1
- except Exception as e:
+ except requests.exceptions.RequestException as e:
failure_count += 1
tqdm.write(f" FAILED - {study_uuid} (error: {str(e)})")
- if isinstance(e, requests.exceptions.RequestException) and e.response is not None:
+ if e.response is not None:
tqdm.write("Response body: " + repr(e.response.text)) # repr for cheap escaping
tqdm.write("") # emtpy newline between errors for legibility
+ except KeyError as e:
+ failure_count += 1
+ tqdm.write(f" FAILED - {study_uuid} (missing field: {str(e)})")
+ tqdm.write("")🧰 Tools🪛 Ruff (0.15.12)[warning] 73-73: Do not catch blind exception: (BLE001) [warning] 75-75: Use explicit conversion flag Replace with conversion flag (RUF010) 🤖 Prompt for AI Agents |
||
| tqdm.write("Response body: " + repr(e.response.text)) # repr for cheap escaping | ||
| tqdm.write("") # emtpy newline between errors for legibility | ||
|
|
||
| print(f"\nDone. {success_count} succeeded, {failure_count} failed.") | ||
|
|
||
|
|
||
| if len(sys.argv) < 2: | ||
| print("Usage: python invalidate_unmodified_studies.py <duration> [--dry-run] [--limit <n>]") | ||
| print("Example: python invalidate_unmodified_studies.py P365D --limit 10 --dry-run") | ||
| sys.exit(1) | ||
|
|
||
| duration_arg = sys.argv[1] | ||
| dry_run_arg = "--dry-run" in sys.argv | ||
|
|
||
| limit_arg = None | ||
| if "--limit" in sys.argv: | ||
| limit_index = sys.argv.index("--limit") | ||
| if limit_index + 1 >= len(sys.argv): | ||
| print("Error: --limit requires a numeric value.") | ||
| sys.exit(1) | ||
| try: | ||
| limit_arg = int(sys.argv[limit_index + 1]) | ||
| if limit_arg <= 0: | ||
| raise ValueError | ||
| except ValueError: | ||
| print("Error: --limit must be a positive integer.") | ||
| sys.exit(1) | ||
|
|
||
| invalidate_unmodified_studies(duration_arg, dry_run=dry_run_arg, limit=limit_arg) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: gridsuite/admin-tools
Length of output: 104
🏁 Script executed:
Repository: gridsuite/admin-tools
Length of output: 21172
🏁 Script executed:
Repository: gridsuite/admin-tools
Length of output: 697
🏁 Script executed:
#!/bin/bash cat -n scripts/unmount_unmodified_studies.pyRepository: gridsuite/admin-tools
Length of output: 4391
🏁 Script executed:
Repository: gridsuite/admin-tools
Length of output: 47
Add timeout to HTTP request for fetching unmodified studies.
The
requests.get()call on line 34 lacks a timeout parameter and can hang indefinitely, causing the job to stall. Addtimeout=30to the request.Proposed fix
🧰 Tools
🪛 Ruff (0.15.10)
[error] 34-34: Probable use of
requestscall without timeout(S113)
🤖 Prompt for AI Agents