-
Notifications
You must be signed in to change notification settings - Fork 0
272 dashboard create one endpoint for articles with proper status #357
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
Changes from 6 commits
0fac0c9
d39d7a8
4ca1b56
0d51089
53117ea
9a55982
9b17eb4
920c75b
045447b
5a58d8b
5b6701e
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 |
|---|---|---|
|
|
@@ -13,7 +13,7 @@ | |
| from django.utils.html import strip_tags | ||
|
|
||
| from jdhapi.utils.doi import get_doi_url_formatted_jdh | ||
| from jdhapi.models import Author, Tag | ||
| from jdhapi.models import Article, Author, Tag | ||
|
|
||
| from jdhseo.utils import getReferencesFromJupyterNotebook | ||
| from requests.exceptions import HTTPError | ||
|
|
@@ -217,6 +217,18 @@ def get_citation(raw_url, article): | |
| } | ||
|
|
||
|
|
||
| def save_citation(article_id): | ||
| logger.info(f"save_article_citation:{article_id}") | ||
|
Collaborator
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. If you want to write the name of the function, write instead 'save_citation', so that for debugging we know exactly what to look for :) |
||
| try: | ||
| article = Article.objects.get(pk=article_id) | ||
| except Article.DoesNotExist: | ||
| logger.error(f"save_article_citation:{article_id} not found") | ||
| return | ||
|
Collaborator
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. maybe here do a |
||
| citation = get_citation(raw_url=article.notebook_ipython_url, article=article) | ||
| article.citation = citation | ||
| article.save() | ||
|
|
||
|
|
||
| def get_raw_from_github( | ||
| repository_url, file_type, host="https://raw.githubusercontent.com" | ||
| ): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,15 @@ | ||
| #!/usr/bin/env python3 | ||
| import logging | ||
| import os | ||
| import sys | ||
|
||
| import time | ||
| from datetime import datetime, timezone | ||
| import requests | ||
| from pathlib import Path | ||
| from urllib.parse import urlparse | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| def trigger_workflow(repo_url, workflow_filename, token=None, ref="main"): | ||
| """ | ||
|
|
@@ -13,11 +18,112 @@ def trigger_workflow(repo_url, workflow_filename, token=None, ref="main"): | |
| :param workflow_filename: Filename of the workflow in .github/workflows (e.g. "hello-world.yml") | ||
| :param ref: Git ref (branch or tag) to run the workflow on | ||
| """ | ||
|
||
| if not token: | ||
| from jdh.settings import GITHUB_ACCESS_TOKEN | ||
| token = _get_github_token(token) | ||
| owner, repo = _parse_owner_repo(repo_url) | ||
|
|
||
| url = f"https://api.github.com/repos/{owner}/{repo}/actions/workflows/{workflow_filename}/dispatches" | ||
| headers = { | ||
| "Authorization": f"Bearer {token}", | ||
| "Accept": "application/vnd.github+json", | ||
| } | ||
| payload = {"ref": ref} | ||
| try: | ||
| resp = requests.post(url, json=payload, headers=headers, timeout=10) | ||
|
||
| if resp.status_code == 204: | ||
| logger.info( | ||
| "Workflow '%s' dispatched on ref '%s' for %s/%s.", | ||
| workflow_filename, | ||
| ref, | ||
| owner, | ||
| repo, | ||
| ) | ||
| return | ||
|
|
||
| logger.error( | ||
| "Failed to dispatch workflow '%s' (%s): %s", | ||
| workflow_filename, | ||
| resp.status_code, | ||
| resp.text, | ||
| ) | ||
|
||
| resp.raise_for_status() | ||
| except requests.RequestException as e: | ||
| logger.error("Workflow dispatch failed: %s", e) | ||
| raise | ||
|
|
||
|
|
||
| def trigger_workflow_and_wait( | ||
| repo_url, | ||
| workflow_filename, | ||
| token=None, | ||
| ref="main", | ||
| timeout_seconds=600, | ||
| poll_interval_seconds=5, | ||
| ): | ||
| token = _get_github_token(token) | ||
| owner, repo = _parse_owner_repo(repo_url) | ||
| started_at = datetime.now(timezone.utc) | ||
|
|
||
| trigger_workflow(repo_url, workflow_filename, token=token, ref=ref) | ||
|
|
||
| runs_url = ( | ||
| f"https://api.github.com/repos/{owner}/{repo}/actions/workflows/" | ||
| f"{workflow_filename}/runs" | ||
| ) | ||
| headers = { | ||
| "Authorization": f"Bearer {token}", | ||
| "Accept": "application/vnd.github+json", | ||
| } | ||
|
|
||
| deadline = time.time() + timeout_seconds | ||
| run_id = None | ||
|
|
||
| token = GITHUB_ACCESS_TOKEN | ||
| while time.time() < deadline: | ||
| try: | ||
| resp = requests.get(runs_url, headers=headers, timeout=10) | ||
| resp.raise_for_status() | ||
| data = resp.json() | ||
| except requests.RequestException as e: | ||
| logger.error("Failed to list workflow runs: %s", e) | ||
| raise | ||
|
|
||
| for run in data.get("workflow_runs", []): | ||
| created_at = _parse_github_datetime(run.get("created_at")) | ||
| if not created_at: | ||
| continue | ||
| if ( | ||
| run.get("event") == "workflow_dispatch" | ||
| and run.get("head_branch") == ref | ||
| and created_at >= started_at | ||
| ): | ||
| run_id = run.get("id") | ||
| status = run.get("status") | ||
| conclusion = run.get("conclusion") | ||
|
|
||
| if status == "completed": | ||
| if conclusion == "success": | ||
| logger.info( | ||
| "Workflow '%s' completed successfully for %s/%s.", | ||
| workflow_filename, | ||
| owner, | ||
| repo, | ||
| ) | ||
| return | ||
| raise RuntimeError( | ||
| f"Workflow '{workflow_filename}' завершён: {conclusion}" | ||
|
||
| ) | ||
| break | ||
|
|
||
| if run_id is None: | ||
| logger.info("Waiting for workflow run to start...") | ||
|
|
||
| time.sleep(poll_interval_seconds) | ||
|
|
||
| raise TimeoutError( | ||
| f"Workflow '{workflow_filename}' did not complete within {timeout_seconds}s" | ||
| ) | ||
|
|
||
|
|
||
| def _parse_owner_repo(repo_url): | ||
| parsed = urlparse(repo_url) | ||
| path = parsed.path.lstrip("/") | ||
|
|
||
|
|
@@ -26,19 +132,25 @@ def trigger_workflow(repo_url, workflow_filename, token=None, ref="main"): | |
|
|
||
| parts = path.split("/") | ||
| if len(parts) >= 2: | ||
| owner = parts[0] | ||
| repo = parts[1] | ||
| return parts[0], parts[1] | ||
|
||
|
|
||
| url = f"https://api.github.com/repos/{owner}/{repo}/actions/workflows/{workflow_filename}/dispatches" | ||
| headers = { | ||
| "Authorization": f"Bearer {token}", | ||
| "Accept": "application/vnd.github+json", | ||
| } | ||
| payload = {"ref": ref} | ||
| resp = requests.post(url, json=payload, headers=headers) | ||
| if resp.status_code == 204: | ||
| print(f"Workflow '{workflow_filename}' dispatched on ref '{ref}'.") | ||
| else: | ||
| print(f"Failed to dispatch workflow: {resp.status_code}") | ||
| print(resp.json()) | ||
| resp.raise_for_status() | ||
| raise ValueError(f"Invalid repository URL: '{repo_url}'") | ||
|
|
||
|
|
||
| def _get_github_token(token): | ||
| if token: | ||
| return token | ||
| from jdh.settings import GITHUB_ACCESS_TOKEN | ||
|
|
||
| return GITHUB_ACCESS_TOKEN | ||
|
||
|
|
||
|
|
||
| def _parse_github_datetime(value): | ||
| if not value: | ||
| return None | ||
| try: | ||
| return datetime.strptime(value, "%Y-%m-%dT%H:%M:%SZ").replace( | ||
| tzinfo=timezone.utc | ||
| ) | ||
| except ValueError: | ||
| return None | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,3 +3,4 @@ | |
| from .send_to_ojs import * | ||
| from .social_media import * | ||
| from .update_article import * | ||
| from .copy_editing import * | ||
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.
We should add SOCIAL_MEDIA and ARCHIVED.