stones/scripts/moloch_dao/import_raid_guild_csv.py

210 lines
7.0 KiB
Python

#!/usr/bin/env python3
"""
Import Raid Guild Members from CSV
This script imports Raid Guild DAO members from a CSV file exported from DAOhaus.
It adds the members to the database with proper DAO membership records and notes.
Usage:
python import_raid_guild_csv.py
"""
import os
import sys
import csv
import logging
from typing import Dict, Any, List, Optional
from datetime import datetime
from dotenv import load_dotenv
# Add parent directory to path to import utils
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from utils.db_connector import DatabaseConnector
from utils.logger import setup_logger
# Load environment variables
load_dotenv()
# Setup logging
logger = setup_logger("raid_guild_importer")
class RaidGuildImporter:
"""Importer for Raid Guild members from CSV file"""
def __init__(self, csv_path: str):
"""Initialize the importer"""
self.csv_path = csv_path
# Initialize database
self.db = DatabaseConnector()
# Register data source
self.data_source_id = self.register_data_source()
def register_data_source(self) -> str:
"""Register the data source in the database"""
query = """
INSERT INTO "DataSource" (
id, name, type, description, "createdAt", "updatedAt"
)
VALUES (
gen_random_uuid(), %(name)s, %(type)s, %(description)s, NOW(), NOW()
)
ON CONFLICT (name) DO UPDATE
SET type = EXCLUDED.type,
description = EXCLUDED.description,
"updatedAt" = NOW()
RETURNING id
"""
result = self.db.execute_query(query, {
"name": "Raid Guild DAO CSV",
"description": "Raid Guild is a Moloch DAO on Gnosis Chain with 151 members. Imported from CSV export.",
"type": "blockchain"
})
data_source_id = result[0]["id"]
logger.info(f"Registered data source with ID: {data_source_id}")
return data_source_id
def read_csv(self) -> List[Dict[str, Any]]:
"""Read the CSV file and return a list of members"""
members = []
try:
with open(self.csv_path, 'r') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
# Only include members that exist and haven't ragequit
if row.get('exists', '').lower() == 'true' and row.get('didRagequit', '').lower() == 'false':
members.append({
"address": row.get('memberAddress', '').lower(),
"delegateKey": row.get('delegateKey', '').lower(),
"shares": int(row.get('shares', 0)),
"loot": int(row.get('loot', 0)),
"joined_at": row.get('createdAt', None)
})
except Exception as e:
logger.error(f"Error reading CSV file: {e}")
raise
logger.info(f"Read {len(members)} members from CSV file")
return members
def process_member(self, member: Dict[str, Any]) -> Optional[str]:
"""Process a member and add to the database"""
address = member["address"]
# Check if contact already exists
query = 'SELECT id FROM "Contact" WHERE "ethereumAddress" = %(address)s'
result = self.db.execute_query(query, {"address": address})
if result:
contact_id = result[0]["id"]
logger.info(f"Contact already exists for {address} with ID {contact_id}")
else:
# Create new contact
query = """
INSERT INTO "Contact" (
id, "ethereumAddress", name, "createdAt", "updatedAt"
)
VALUES (
gen_random_uuid(), %(address)s, %(name)s, NOW(), NOW()
)
RETURNING id
"""
result = self.db.execute_query(query, {
"address": address,
"name": f"Raid Guild Member"
})
if not result:
logger.error(f"Failed to add contact for {address}")
return None
contact_id = result[0]["id"]
logger.info(f"Added new contact: {address} with ID {contact_id}")
# Add DAO membership
query = """
INSERT INTO "DaoMembership" (
id, "contactId", "daoName", "daoType", "joinedAt", "createdAt", "updatedAt"
)
VALUES (
gen_random_uuid(), %(contact_id)s, %(dao_name)s, %(dao_type)s,
%(joined_at)s, NOW(), NOW()
)
ON CONFLICT ("contactId", "daoName") DO UPDATE
SET "daoType" = EXCLUDED."daoType",
"joinedAt" = EXCLUDED."joinedAt",
"updatedAt" = NOW()
"""
joined_at = None
if member.get("joined_at"):
try:
# Convert Unix timestamp to datetime
joined_at_timestamp = int(member["joined_at"])
joined_at = datetime.fromtimestamp(joined_at_timestamp)
except (ValueError, TypeError):
joined_at = None
self.db.execute_update(query, {
"contact_id": contact_id,
"dao_name": "Raid Guild",
"dao_type": "Moloch DAO",
"joined_at": joined_at
})
# Add a note about the member's shares and loot
query = """
INSERT INTO "Note" (
id, "contactId", content, "createdAt", "updatedAt"
)
VALUES (
gen_random_uuid(), %(contact_id)s, %(content)s, NOW(), NOW()
)
"""
self.db.execute_update(query, {
"contact_id": contact_id,
"content": f"Member of Raid Guild DAO (0xfe1084bc16427e5eb7f13fc19bcd4e641f7d571f) with {member['shares']} shares and {member['loot']} loot"
})
return contact_id
def run(self):
"""Run the importer"""
logger.info(f"Starting Raid Guild member import from {self.csv_path}")
# Read members from CSV
members = self.read_csv()
# Process members
processed_count = 0
for member in members:
if self.process_member(member):
processed_count += 1
logger.info(f"Processed {processed_count} members out of {len(members)} found")
return processed_count
def main():
"""Main function"""
try:
csv_path = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"raid-guild-hkr_Members_1742163047.csv"
)
importer = RaidGuildImporter(csv_path)
processed_count = importer.run()
logger.info(f"Import completed successfully. Processed {processed_count} members.")
return 0
except Exception as e:
logger.exception(f"Error running importer: {e}")
return 1
if __name__ == "__main__":
sys.exit(main())