Skip to content

Commit 463ba67

Browse files
committed
Adding functionality to query sivacor
1 parent c1337c7 commit 463ba67

File tree

4 files changed

+406
-1
lines changed

4 files changed

+406
-1
lines changed

bitbucket-pipelines.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,27 @@ pipelines:
347347
- git add -f generated/*
348348
- git commit -m "[skip ci] Downloaded Box files and created manifests"
349349
- git push
350+
8-download-sivacor: #name of this pipeline
351+
- variables: #list variable names under here
352+
- name: jiraticket
353+
default: ""
354+
- step:
355+
image: python:3.12
356+
name: Download SIVACOR artifacts
357+
caches:
358+
- pip
359+
script:
360+
- if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
361+
- . ./tools/parse_yaml.sh
362+
- eval $(parse_yaml config.yml)
363+
# Determine Jira case: variable > directory name > config.yml
364+
- if [ -z "$jiraticket" ]; then jiraticket=$(basename $(pwd)); fi
365+
- if [ -z "$jiraticket" ]; then echo "Error: Could not determine Jira case"; exit 1; fi
366+
- echo "Using Jira case: $jiraticket"
367+
# Run the SIVACOR download script
368+
- python3 tools/download_sivacor.py $jiraticket
369+
# Push the created branch (do not merge to master)
370+
- git push -u origin HEAD
350371
w-big-populate-from-icpsr: #name of this pipeline
351372
- variables: #list variable names under here
352373
# These do not need to have a value, if "config.yml" is filled out.

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ zenodo-get>=1.6.0
44
osfclient
55
boxsdk[jwt]==3.14.0
66
python-dotenv
7+
jira
8+
sivacor==0.1.9

tools/download_sivacor.py

Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
#!/usr/bin/python3
2+
"""
3+
Download artifacts from SIVACOR submission.
4+
5+
This script downloads all artifacts associated with a SIVACOR submission ID,
6+
handles ZIP file extraction, and commits the results to a git branch.
7+
8+
Usage:
9+
python3 tools/download_sivacor.py [JIRA_CASE]
10+
11+
Examples:
12+
# Using current directory name as Jira case
13+
python3 tools/download_sivacor.py
14+
15+
# Specifying Jira case explicitly
16+
python3 tools/download_sivacor.py aearep-8885
17+
18+
# Using config.yml (jiraticket field)
19+
python3 tools/download_sivacor.py
20+
21+
Arguments:
22+
JIRA_CASE - Jira case number (optional)
23+
Order of precedence:
24+
1. Command line argument
25+
2. Current directory name
26+
3. config.yml jiraticket field
27+
28+
How it works:
29+
1. Determines Jira case from argument, directory name, or config.yml
30+
2. Looks up SIVACOR ID from Jira ticket
31+
3. Determines target folder from config.yml (precedence: openicpsr > zenodo > dataverse > osf)
32+
4. Downloads all artifacts using sivacor CLI
33+
5. If ZIP file exists:
34+
- Clears folder contents (keeping folder)
35+
- Unpacks ZIP
36+
- Downloads other artifacts
37+
6. Creates branch sivacor-{sivacor_id} (lowercase)
38+
7. Git adds and commits the folder
39+
40+
Environment Variables Required:
41+
JIRA_USERNAME - Your Jira email address
42+
JIRA_API_KEY - API token from https://id.atlassian.com/manage-profile/security/api-tokens
43+
SIVACOR environment variables as required by sivacor CLI
44+
45+
Requirements:
46+
- jira: Python Jira library
47+
- yaml: YAML configuration file support
48+
- sivacor: SIVACOR CLI tool (installed via pip)
49+
50+
Error Handling:
51+
- Exits with error if SIVACOR ID field is not populated in Jira
52+
- Validates target folder existence
53+
- Handles missing config.yml gracefully
54+
55+
Authors: Lars Vilhuber
56+
Version: 2026-02-12
57+
"""
58+
59+
import os
60+
import sys
61+
import subprocess
62+
import yaml
63+
import zipfile
64+
import glob
65+
import shutil
66+
67+
version = "2026-02-12"
68+
69+
print(f"SIVACOR downloader v{version}")
70+
71+
72+
def get_jira_case():
73+
"""
74+
Determine Jira case number.
75+
76+
Order of precedence:
77+
1. Command line argument
78+
2. Current directory name
79+
3. config.yml jiraticket field
80+
81+
Returns:
82+
Jira case number (e.g., 'aearep-8885')
83+
"""
84+
# Check command line
85+
if len(sys.argv) >= 2:
86+
return sys.argv[1].lower()
87+
88+
# Check current directory name
89+
current_dir = os.path.basename(os.getcwd())
90+
if current_dir.startswith('aearep-'):
91+
return current_dir.lower()
92+
93+
# Check config.yml
94+
try:
95+
with open("config.yml") as f:
96+
config = next(yaml.load_all(f, Loader=yaml.FullLoader))
97+
jira_ticket = config.get("jiraticket")
98+
if jira_ticket:
99+
return jira_ticket.lower()
100+
except FileNotFoundError:
101+
pass
102+
103+
print("Error: Could not determine Jira case number")
104+
print(f"Usage: {sys.argv[0]} [JIRA_CASE]")
105+
print(f"Or run from a directory named aearep-XXXX")
106+
print(f"Or specify jiraticket in config.yml")
107+
sys.exit(1)
108+
109+
110+
def get_sivacor_id(jira_case):
111+
"""
112+
Get SIVACOR ID from Jira using jira_get_info.py
113+
114+
Args:
115+
jira_case: Jira case number (e.g., 'aearep-8885')
116+
117+
Returns:
118+
SIVACOR ID string
119+
"""
120+
script_dir = os.path.dirname(os.path.abspath(__file__))
121+
jira_script = os.path.join(script_dir, "jira_get_info.py")
122+
123+
try:
124+
result = subprocess.run(
125+
[sys.executable, jira_script, jira_case, "sivacorid"],
126+
capture_output=True,
127+
text=True,
128+
check=False
129+
)
130+
131+
sivacor_id = result.stdout.strip()
132+
133+
if not sivacor_id:
134+
print(f"Error: SIVACOR ID field is not populated in Jira ticket {jira_case}")
135+
print(f"Please ensure the 'SIVACOR ID' field is set in the Jira ticket")
136+
sys.exit(1)
137+
138+
return sivacor_id
139+
140+
except Exception as e:
141+
print(f"Error retrieving SIVACOR ID from Jira: {e}")
142+
sys.exit(1)
143+
144+
145+
def get_target_folder():
146+
"""
147+
Get target folder from config.yml
148+
149+
Precedence: openicpsr > zenodo > dataverse > osf
150+
151+
Returns:
152+
Folder name (e.g., '222222')
153+
"""
154+
try:
155+
with open("config.yml") as f:
156+
config = next(yaml.load_all(f, Loader=yaml.FullLoader))
157+
158+
# Check in order of precedence
159+
for key in ['openicpsr', 'zenodo', 'dataverse', 'osf']:
160+
value = config.get(key)
161+
if value and str(value).strip():
162+
return str(value).strip()
163+
164+
print("Error: No repository folder found in config.yml")
165+
print("Please specify one of: openicpsr, zenodo, dataverse, or osf")
166+
sys.exit(1)
167+
168+
except FileNotFoundError:
169+
print("Error: config.yml not found")
170+
sys.exit(1)
171+
172+
173+
def download_sivacor_artifacts(sivacor_id, target_folder):
174+
"""
175+
Download all artifacts from SIVACOR submission
176+
177+
Args:
178+
sivacor_id: SIVACOR submission ID
179+
target_folder: Target directory for downloads
180+
"""
181+
print(f"\n{'=' * 60}")
182+
print(f"Downloading artifacts from SIVACOR ID: {sivacor_id}")
183+
print(f"Target folder: {target_folder}")
184+
print(f"{'=' * 60}\n")
185+
186+
# Create target folder if it doesn't exist
187+
os.makedirs(target_folder, exist_ok=True)
188+
189+
# Download all artifacts using sivacor CLI
190+
# The sivacor CLI downloads to current directory, so we need to cd into target folder
191+
original_dir = os.getcwd()
192+
193+
try:
194+
os.chdir(target_folder)
195+
196+
print("Downloading all artifacts...")
197+
result = subprocess.run(
198+
[sys.executable, "-m", "sivacor", "submission", "get", sivacor_id, "--download", "all"],
199+
check=True
200+
)
201+
202+
os.chdir(original_dir)
203+
204+
except subprocess.CalledProcessError as e:
205+
os.chdir(original_dir)
206+
print(f"Error downloading from SIVACOR: {e}")
207+
sys.exit(1)
208+
209+
210+
def handle_zip_files(target_folder):
211+
"""
212+
Handle ZIP file extraction in target folder
213+
214+
If a ZIP file exists:
215+
- Clear folder contents (keeping folder)
216+
- Unpack ZIP
217+
- Keep other downloaded artifacts
218+
219+
Args:
220+
target_folder: Target directory containing downloads
221+
"""
222+
# Find ZIP files
223+
zip_files = glob.glob(os.path.join(target_folder, "*.zip"))
224+
225+
if not zip_files:
226+
print("No ZIP files found, keeping all downloaded artifacts")
227+
return
228+
229+
if len(zip_files) > 1:
230+
print(f"Warning: Found multiple ZIP files: {zip_files}")
231+
print(f"Using first ZIP file: {zip_files[0]}")
232+
233+
zip_file = zip_files[0]
234+
print(f"\nFound ZIP file: {os.path.basename(zip_file)}")
235+
print(f"Clearing folder contents and unpacking ZIP...")
236+
237+
# Get list of all files before clearing
238+
all_files = [f for f in os.listdir(target_folder) if os.path.isfile(os.path.join(target_folder, f))]
239+
non_zip_files = [f for f in all_files if not f.endswith('.zip')]
240+
241+
# Move ZIP to temp location outside the folder
242+
temp_dir = os.path.dirname(target_folder)
243+
temp_zip = os.path.join(temp_dir, os.path.basename(zip_file) + ".tmp")
244+
shutil.move(zip_file, temp_zip)
245+
246+
# Clear folder contents
247+
for item in os.listdir(target_folder):
248+
item_path = os.path.join(target_folder, item)
249+
if os.path.isfile(item_path):
250+
os.remove(item_path)
251+
elif os.path.isdir(item_path):
252+
shutil.rmtree(item_path)
253+
254+
# Extract ZIP directly from temp location
255+
print(f"Extracting ZIP file...")
256+
try:
257+
with zipfile.ZipFile(temp_zip, 'r') as zip_ref:
258+
zip_ref.extractall(target_folder)
259+
print(f"ZIP extracted successfully")
260+
261+
# Remove the temp ZIP file after extraction
262+
os.remove(temp_zip)
263+
264+
except Exception as e:
265+
print(f"Error extracting ZIP file: {e}")
266+
# Try to restore the ZIP file if extraction failed
267+
if os.path.exists(temp_zip):
268+
shutil.move(temp_zip, zip_file)
269+
sys.exit(1)
270+
271+
# Note about other artifacts
272+
if non_zip_files:
273+
print(f"Note: Other artifacts were cleared as they may be included in the ZIP")
274+
275+
276+
def git_commit_changes(sivacor_id, target_folder, jira_case):
277+
"""
278+
Create git branch and commit changes
279+
280+
Args:
281+
sivacor_id: SIVACOR submission ID (for branch name)
282+
target_folder: Folder to git add
283+
jira_case: Jira case number (for commit message)
284+
"""
285+
branch_name = f"sivacor-{sivacor_id.lower()}"
286+
287+
print(f"\n{'=' * 60}")
288+
print(f"Git operations:")
289+
print(f"Branch: {branch_name}")
290+
print(f"{'=' * 60}\n")
291+
292+
# Check if branch exists
293+
result = subprocess.run(
294+
["git", "rev-parse", "--verify", branch_name],
295+
capture_output=True,
296+
check=False
297+
)
298+
299+
if result.returncode == 0:
300+
# Branch exists, check it out
301+
print(f"Branch {branch_name} exists, checking out...")
302+
subprocess.run(["git", "checkout", branch_name], check=True)
303+
else:
304+
# Create new branch
305+
print(f"Creating new branch {branch_name}...")
306+
subprocess.run(["git", "checkout", "-b", branch_name], check=True)
307+
308+
# Git add the folder
309+
print(f"Adding {target_folder} to git...")
310+
subprocess.run(["git", "add", target_folder], check=True)
311+
312+
# Check if there are changes to commit
313+
result = subprocess.run(
314+
["git", "diff", "--cached", "--quiet"],
315+
check=False
316+
)
317+
318+
if result.returncode == 0:
319+
print("No changes to commit")
320+
return
321+
322+
# Commit
323+
commit_message = f"[sivacor] Adding artifacts from SIVACOR ID {sivacor_id}"
324+
print(f"Committing with message: {commit_message}")
325+
subprocess.run(["git", "commit", "-m", commit_message], check=True)
326+
327+
print(f"\n{'=' * 60}")
328+
print(f"SUCCESS!")
329+
print(f"Branch: {branch_name}")
330+
print(f"Folder: {target_folder}")
331+
print(f"SIVACOR ID: {sivacor_id}")
332+
print(f"{'=' * 60}\n")
333+
334+
335+
def main():
336+
"""Main execution function"""
337+
338+
# Step 1: Determine Jira case
339+
jira_case = get_jira_case()
340+
print(f"Jira case: {jira_case}")
341+
342+
# Step 2: Get SIVACOR ID from Jira
343+
sivacor_id = get_sivacor_id(jira_case)
344+
print(f"SIVACOR ID: {sivacor_id}")
345+
346+
# Step 3: Get target folder from config.yml
347+
target_folder = get_target_folder()
348+
print(f"Target folder: {target_folder}")
349+
350+
# Step 4: Download artifacts
351+
download_sivacor_artifacts(sivacor_id, target_folder)
352+
353+
# Step 5: Handle ZIP files
354+
handle_zip_files(target_folder)
355+
356+
# Step 6: Git operations
357+
git_commit_changes(sivacor_id, target_folder, jira_case)
358+
359+
360+
if __name__ == '__main__':
361+
main()

0 commit comments

Comments
 (0)