156 lines
4.2 KiB
Python
156 lines
4.2 KiB
Python
|
|
#!/usr/bin/env python
|
||
|
|
"""
|
||
|
|
Migration script to reorganize studies into topic-based subfolders.
|
||
|
|
|
||
|
|
Run with --dry-run first to preview changes:
|
||
|
|
python migrate_studies_to_topics.py --dry-run
|
||
|
|
|
||
|
|
Then run without flag to execute:
|
||
|
|
python migrate_studies_to_topics.py
|
||
|
|
"""
|
||
|
|
|
||
|
|
import shutil
|
||
|
|
import argparse
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
STUDIES_DIR = Path(__file__).parent / "studies"
|
||
|
|
|
||
|
|
# Topic classification based on study name prefixes
|
||
|
|
TOPIC_MAPPING = {
|
||
|
|
'bracket_': 'Simple_Bracket',
|
||
|
|
'drone_gimbal_': 'Drone_Gimbal',
|
||
|
|
'm1_mirror_': 'M1_Mirror',
|
||
|
|
'uav_arm_': 'UAV_Arm',
|
||
|
|
'simple_beam_': 'Simple_Beam',
|
||
|
|
}
|
||
|
|
|
||
|
|
# Files/folders to skip (not studies)
|
||
|
|
SKIP_ITEMS = {
|
||
|
|
'm1_mirror_all_trials_export.csv', # Data export file
|
||
|
|
'.gitkeep',
|
||
|
|
'__pycache__',
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def classify_study(study_name: str) -> str:
|
||
|
|
"""Determine which topic folder a study belongs to."""
|
||
|
|
for prefix, topic in TOPIC_MAPPING.items():
|
||
|
|
if study_name.startswith(prefix):
|
||
|
|
return topic
|
||
|
|
return '_Other'
|
||
|
|
|
||
|
|
|
||
|
|
def get_studies_to_migrate():
|
||
|
|
"""Get list of studies that need migration (not already in topic folders)."""
|
||
|
|
studies = []
|
||
|
|
|
||
|
|
for item in STUDIES_DIR.iterdir():
|
||
|
|
# Skip non-directories and special items
|
||
|
|
if not item.is_dir():
|
||
|
|
continue
|
||
|
|
if item.name in SKIP_ITEMS:
|
||
|
|
continue
|
||
|
|
if item.name.startswith('.'):
|
||
|
|
continue
|
||
|
|
|
||
|
|
# Check if this is already a topic folder (contains study subdirs)
|
||
|
|
# A topic folder would have subdirs with 1_setup folders
|
||
|
|
is_topic_folder = any(
|
||
|
|
(sub / "1_setup").exists()
|
||
|
|
for sub in item.iterdir()
|
||
|
|
if sub.is_dir()
|
||
|
|
)
|
||
|
|
|
||
|
|
if is_topic_folder:
|
||
|
|
print(f"[SKIP] {item.name} - already a topic folder")
|
||
|
|
continue
|
||
|
|
|
||
|
|
# Check if this is a study (has 1_setup or optimization_config.json)
|
||
|
|
is_study = (
|
||
|
|
(item / "1_setup").exists() or
|
||
|
|
(item / "optimization_config.json").exists()
|
||
|
|
)
|
||
|
|
|
||
|
|
if is_study:
|
||
|
|
topic = classify_study(item.name)
|
||
|
|
studies.append({
|
||
|
|
'name': item.name,
|
||
|
|
'source': item,
|
||
|
|
'topic': topic,
|
||
|
|
'target': STUDIES_DIR / topic / item.name
|
||
|
|
})
|
||
|
|
else:
|
||
|
|
print(f"[SKIP] {item.name} - not a study (no 1_setup folder)")
|
||
|
|
|
||
|
|
return studies
|
||
|
|
|
||
|
|
|
||
|
|
def migrate_studies(dry_run: bool = True):
|
||
|
|
"""Migrate studies to topic folders."""
|
||
|
|
studies = get_studies_to_migrate()
|
||
|
|
|
||
|
|
if not studies:
|
||
|
|
print("\nNo studies to migrate. All studies are already organized.")
|
||
|
|
return
|
||
|
|
|
||
|
|
# Group by topic for display
|
||
|
|
by_topic = {}
|
||
|
|
for s in studies:
|
||
|
|
if s['topic'] not in by_topic:
|
||
|
|
by_topic[s['topic']] = []
|
||
|
|
by_topic[s['topic']].append(s)
|
||
|
|
|
||
|
|
print("\n" + "="*60)
|
||
|
|
print("MIGRATION PLAN")
|
||
|
|
print("="*60)
|
||
|
|
|
||
|
|
for topic in sorted(by_topic.keys()):
|
||
|
|
print(f"\n{topic}/")
|
||
|
|
for s in by_topic[topic]:
|
||
|
|
print(f" +-- {s['name']}/")
|
||
|
|
|
||
|
|
print(f"\nTotal: {len(studies)} studies to migrate")
|
||
|
|
|
||
|
|
if dry_run:
|
||
|
|
print("\n[DRY RUN] No changes made. Run without --dry-run to execute.")
|
||
|
|
return
|
||
|
|
|
||
|
|
# Execute migration
|
||
|
|
print("\n" + "="*60)
|
||
|
|
print("EXECUTING MIGRATION")
|
||
|
|
print("="*60)
|
||
|
|
|
||
|
|
# Create topic folders
|
||
|
|
created_topics = set()
|
||
|
|
for s in studies:
|
||
|
|
topic_dir = STUDIES_DIR / s['topic']
|
||
|
|
if s['topic'] not in created_topics:
|
||
|
|
topic_dir.mkdir(exist_ok=True)
|
||
|
|
created_topics.add(s['topic'])
|
||
|
|
print(f"[CREATE] {s['topic']}/")
|
||
|
|
|
||
|
|
# Move studies
|
||
|
|
for s in studies:
|
||
|
|
try:
|
||
|
|
shutil.move(str(s['source']), str(s['target']))
|
||
|
|
print(f"[MOVE] {s['name']} -> {s['topic']}/{s['name']}")
|
||
|
|
except Exception as e:
|
||
|
|
print(f"[ERROR] Failed to move {s['name']}: {e}")
|
||
|
|
|
||
|
|
print("\n" + "="*60)
|
||
|
|
print("MIGRATION COMPLETE")
|
||
|
|
print("="*60)
|
||
|
|
|
||
|
|
|
||
|
|
def main():
|
||
|
|
parser = argparse.ArgumentParser(description="Migrate studies to topic folders")
|
||
|
|
parser.add_argument('--dry-run', action='store_true',
|
||
|
|
help='Preview changes without executing')
|
||
|
|
args = parser.parse_args()
|
||
|
|
|
||
|
|
migrate_studies(dry_run=args.dry_run)
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|