Skip to main content

harvard-artifacts-etl-streamlit

ETL pipeline and analytics app for Harvard Art Museums API with SQL storage and Streamlit visualization

Informações da origem

Repositório
reason-machines/data-skills
Última atividade na origem
9 de junho de 2026 às 23:59
Idioma detectado do SKILL.md
inglês
Estrelas
5
Forks
1

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
harvard-artifacts-etl-streamlit
description
ETL pipeline and analytics app for Harvard Art Museums API with SQL storage and Streamlit visualization
triggers
["build an ETL pipeline for museum artifacts","connect to Harvard Art Museums API","create a streamlit dashboard for art data","extract and transform API data to SQL","analyze museum collection with SQL queries","visualize art museum data with plotly","build a data engineering pipeline for artifacts","create analytics app for Harvard art collection"]
# Harvard Artifacts ETL & Analytics Skill > Skill by [ara.so](https://ara.so) — Data Skills collection. This skill enables AI agents to build and work with end-to-end data engineering pipelines using the Harvard Art Museums API. It demonstrates ETL workflows, SQL database design, analytics queries, and interactive Streamlit dashboards with Plotly visualizations. ## What This Project Does The Harvard Artifacts Collection Data Engineering & Analytics App provides: - **API Integration**: Fetch artifact data from Harvard Art Museums API with pagination and rate limiting - **ETL Pipeline**: Extract, transform, and load museum artifact data into relational SQL databases - **SQL Database**: Store artifacts metadata, media details, and color information with proper relationships - **Analytics Queries**: 20+ predefined SQL queries for insights on culture, century, media, colors, and departments - **Interactive Dashboard**: Streamlit-based UI with Plotly visualizations for real-time analytics ## Installation ```bash # Clone the repository git clone https://github.com/Manali0711/Harvard-Artifacts-Collection-Data-Engineering-Analytics-App.git cd Harvard-Artifacts-Collection-Data-Engineering-Analytics-App # Install dependencies pip install -r requirements.txt ``` ### Required Dependencies ```txt streamlit pandas requests mysql-connector-python plotly python-dotenv ``` ## Configuration ### Environment Variables Create a `.env` file or set environment variables: ```bash # Harvard Art Museums API HARVARD_API_KEY=your_api_key_here # MySQL/TiDB Configuration DB_HOST=your_database_host DB_PORT=3306 DB_USER=your_username DB_PASSWORD=your_password DB_NAME=harvard_artifacts ``` ### API Key Setup Get your free API key from [Harvard Art Museums API](https://harvardartmuseums.org/collections/api): ```python import os from dotenv import load_dotenv load_dotenv() API_KEY = os.getenv('HARVARD_API_KEY') ``` ## Database Schema ### Tables Structure ```sql -- Artifact Metadata CREATE TABLE artifactmetadata ( artifact_id INT PRIMARY KEY, title VARCHAR(500), culture VARCHAR(255), century VARCHAR(100), classification VARCHAR(255), department VARCHAR(255), dated VARCHAR(255), description TEXT, accession_number VARCHAR(100), primary_image_url TEXT ); -- Artifact Media CREATE TABLE artifactmedia ( media_id INT AUTO_INCREMENT PRIMARY KEY, artifact_id INT, media_type VARCHAR(50), media_url TEXT, FOREIGN KEY (artifact_id) REFERENCES artifactmetadata(artifact_id) ); -- Artifact Colors CREATE TABLE artifactcolors ( color_id INT AUTO_INCREMENT PRIMARY KEY, artifact_id INT, color_hex VARCHAR(10), color_name VARCHAR(100), percentage FLOAT, FOREIGN KEY (artifact_id) REFERENCES artifactmetadata(artifact_id) ); ``` ## ETL Pipeline Implementation ### 1. Extract Data from API ```python import requests import pandas as pd def fetch_artifacts(api_key, page=1, size=100): """Fetch artifacts from Harvard Art Museums API""" base_url = "https://api.harvardartmuseums.org/object" params = { 'apikey': api_key, 'page': page, 'size': size, 'hasimage': 1 # Only artifacts with images } response = requests.get(base_url, params=params) response.raise_for_status() data = response.json() return data['records'], data['info'] # Paginated extraction def extract_all_artifacts(api_key, max_pages=10): """Extract artifacts with pagination""" all_artifacts = [] for page in range(1, max_pages + 1): records, info = fetch_artifacts(api_key, page=page) all_artifacts.extend(records) if page >= info['pages']: break return all_artifacts ``` ### 2. Transform Data ```python def transform_artifacts(raw_artifacts): """Transform nested JSON to relational format""" metadata_list = [] media_list = [] colors_list = [] for artifact in raw_artifacts: # Metadata extraction metadata = { 'artifact_id': artifact.get('id'), 'title': artifact.get('title', 'Unknown'), 'culture': artifact.get('culture'), 'century': artifact.get('century'), 'classification': artifact.get('classification'), 'department': artifact.get('department'), 'dated': artifact.get('dated'), 'description': artifact.get('description'), 'accession_number': artifact.get('accessionNumber'), 'primary_image_url': artifact.get('primaryimageurl') } metadata_list.append(metadata) # Media extraction if 'images' in artifact and artifact['images']: for img in artifact['images']: media = { 'artifact_id': artifact.get('id'), 'media_type': 'image', 'media_url': img.get('baseimageurl') } media_list.append(media) # Colors extraction if 'colors' in artifact and artifact['colors']: for color in artifact['colors']: color_data = { 'artifact_id': artifact.get('id'), 'color_hex': color.get('hex'), 'color_name': color.get('color'), 'percentage': color.get('percent') } colors_list.append(color_data) return ( pd.DataFrame(metadata_list), pd.DataFrame(media_list), pd.DataFrame(colors_list) ) ``` ### 3. Load to SQL Database ```python import mysql.connector from mysql.connector import Error def create_connection(host, port, user, password, database): """Create database connection""" try: connection = mysql.connector.connect( host=host, port=port, user=user, password=password, database=database ) return connection except Error as e: print(f"Error: {e}") return None def load_to_database(df_metadata, df_media, df_colors, connection): """Batch insert data into SQL database""" cursor = connection.cursor() # Load metadata for _, row in df_metadata.iterrows(): query = """ INSERT INTO artifactmetadata (artifact_id, title, culture, century, classification, department, dated, description, accession_number, primary_image_url) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ON DUPLICATE KEY UPDATE title=VALUES(title) """ cursor.execute(query, tuple(row)) # Load media for _, row in df_media.iterrows(): query = """ INSERT INTO artifactmedia (artifact_id, media_type, media_url) VALUES (%s, %s, %s) """ cursor.execute(query, tuple(row)) # Load colors for _, row in df_colors.iterrows(): query = """ INSERT INTO artifactcolors (artifact_id, color_hex, color_name, percentage) VALUES (%s, %s, %s, %s) """ cursor.execute(query, tuple(row)) connection.commit() cursor.close() ``` ## Analytics Queries ### Sample SQL Analytics ```python ANALYTICS_QUERIES = { "Artifacts by Culture": """ SELECT culture, COUNT(*) as count FROM artifactmetadata WHERE culture IS NOT NULL GROUP BY culture ORDER BY count DESC LIMIT 15 """, "Artifacts by Century": """ SELECT century, COUNT(*) as count FROM artifactmetadata WHERE century IS NOT NULL GROUP BY century ORDER BY count DESC """, "Top Colors in Collection": """ SELECT color_name, COUNT(*) as frequency, AVG(percentage) as avg_percentage FROM artifactcolors GROUP BY color_name ORDER BY frequency DESC LIMIT 20 """, "Department Distribution": """ SELECT department, COUNT(*) as artifact_count FROM artifactmetadata GROUP BY department ORDER BY artifact_count DESC """, "Media Availability": """ SELECT COUNT(DISTINCT am.artifact_id) as total_artifacts, COUNT(DISTINCT m.artifact_id) as artifacts_with_media, ROUND(COUNT(DISTINCT m.artifact_id) * 100.0 / COUNT(DISTINCT am.artifact_id), 2) as coverage_percentage FROM artifactmetadata am LEFT JOIN artifactmedia m ON am.artifact_id = m.artifact_id """ } def execute_query(connection, query): """Execute SQL query and return results as DataFrame""" cursor = connection.cursor(dictionary=True) cursor.execute(query) results = cursor.fetchall() cursor.close() return pd.DataFrame(results) ``` ## Streamlit Dashboard ### Main Application Structure ```python import streamlit as st import plotly.express as px import os st.set_page_config(page_title="Harvard Artifacts Analytics", layout="wide") # Sidebar configuration st.sidebar.title("🎨 Harvard Art Analytics") st.sidebar.markdown("---") # Database connection @st.cache_resource def get_db_connection(): return create_connection( host=os.getenv('DB_HOST'), port=int(os.getenv('DB_PORT', 3306)), user=os.getenv('DB_USER'), password=os.getenv('DB_PASSWORD'), database=os.getenv('DB_NAME') ) # Main app def main(): st.title("📊 Harvard Artifacts Collection Analytics") conn = get_db_connection() # Query selector query_name = st.selectbox( "Select Analytics Query:", list(ANALYTICS_QUERIES.keys()) ) if st.button("Run Query"): with st.spinner("Executing query..."): df_results = execute_query(conn, ANALYTICS_QUERIES[query_name]) # Display results st.subheader("Query Results") st.dataframe(df_results) # Visualization if len(df_results.columns) >= 2: st.subheader("Visualization") fig = px.bar( df_results, x=df_results.columns[0], y=df_results.columns[1], title=query_name ) st.plotly_chart(fig, use_container_width=True) if __name__ == "__main__": main() ``` ## Running the Application ```bash # Start Streamlit app streamlit run app.py # Access dashboard at http://localhost:8501 ``` ## Common Patterns ### Complete ETL Workflow ```python import os from dotenv import load_dotenv load_dotenv() # Configuration API_KEY = os.getenv('HARVARD_API_KEY') DB_CONFIG = { 'host': os.getenv('DB_HOST'), 'port': int(os.getenv('DB_PORT', 3306)), 'user': os.getenv('DB_USER'), 'password': os.getenv('DB_PASSWORD'), 'database': os.getenv('DB_NAME') } # Execute ETL def run_etl_pipeline(): # Extract print("Extracting data from API...") raw_artifacts = extract_all_artifacts(API_KEY, max_pages=5) # Transform print("Transforming data...") df_metadata, df_media, df_colors = transform_artifacts(raw_artifacts) # Load print("Loading to database...") conn = create_connection(**DB_CONFIG) load_to_database(df_metadata, df_media, df_colors, conn) conn.close() print(f"ETL Complete: {len(df_metadata)} artifacts processed") if __name__ == "__main__": run_etl_pipeline() ``` ## Troubleshooting ### API Rate Limiting ```python import time def fetch_with_retry(api_key, page, max_retries=3): """Fetch with exponential backoff""" for attempt in range(max_retries): try: return fetch_artifacts(api_key, page) except requests.exceptions.HTTPError as e: if e.response.status_code == 429: # Too many requests wait_time = 2 ** attempt print(f"Rate limited. Waiting {wait_time}s...") time.sleep(wait_time) else: raise raise Exception("Max retries exceeded") ``` ### Database Connection Issues ```python def verify_connection(connection): """Test database connection""" try: cursor = connection.cursor() cursor.execute("SELECT 1") cursor.fetchone() cursor.close() return True except Error as e: print(f"Connection failed: {e}") return False
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub