grlll Claude commited on
Commit
d72e914
·
0 Parent(s):

Initial commit: Apple Health Landing Zone

Browse files

Create a Hugging Face Space that allows users to:
- Upload Apple Health export.xml files
- Create private datasets for health data storage
- Automatically generate MCP server spaces for data querying

Features OAuth login, private data handling, and MCP integration.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

Files changed (6) hide show
  1. .gitignore +10 -0
  2. .python-version +1 -0
  3. README.md +59 -0
  4. app.py +333 -0
  5. pyproject.toml +18 -0
  6. uv.lock +0 -0
.gitignore ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
.python-version ADDED
@@ -0,0 +1 @@
 
 
1
+ 3.12
README.md ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Apple Health Landing Zone
3
+ emoji: 🏥
4
+ colorFrom: green
5
+ colorTo: blue
6
+ sdk: gradio
7
+ sdk_version: 5.34.0
8
+ app_file: app.py
9
+ pinned: false
10
+ hf_oauth: true
11
+ hf_oauth_scopes:
12
+ - read-repos
13
+ - write-repos
14
+ - manage-repos
15
+ ---
16
+
17
+ # Apple Health Landing Zone
18
+
19
+ Upload your Apple Health export.xml file to create a private data ecosystem on Hugging Face.
20
+
21
+ ## What This Does
22
+
23
+ This space helps you create:
24
+
25
+ 1. **Private Dataset**: Securely stores your Apple Health export.xml file
26
+ 2. **MCP Server Space**: A private Gradio space that acts as an MCP (Model Context Protocol) server for querying your health data
27
+
28
+ ## Features
29
+
30
+ - 🔐 OAuth login with your Hugging Face account
31
+ - 📊 Private dataset creation for your health data
32
+ - 🖥️ Automatic MCP server setup
33
+ - 🔍 Query interface for your health data
34
+ - 🤖 Integration with Claude Desktop via MCP
35
+
36
+ ## How to Use
37
+
38
+ 1. Click "Sign in with Hugging Face"
39
+ 2. Upload your Apple Health export.xml file
40
+ 3. Choose a project name
41
+ 4. Click "Create Landing Zone"
42
+
43
+ ## Getting Your Apple Health Data
44
+
45
+ 1. Open the Health app on your iPhone
46
+ 2. Tap your profile picture in the top right
47
+ 3. Scroll down and tap "Export All Health Data"
48
+ 4. Choose to export and save the file
49
+ 5. Upload the export.xml file here
50
+
51
+ ## Privacy
52
+
53
+ - All created repositories are **private** by default
54
+ - Your health data is only accessible by you
55
+ - The MCP server runs in your own private space
56
+
57
+ ## MCP Integration
58
+
59
+ After creating your landing zone, you can add the MCP server to Claude Desktop by adding the configuration shown in your created space to your Claude Desktop settings.
app.py ADDED
@@ -0,0 +1,333 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from huggingface_hub import HfApi, create_repo
3
+ from huggingface_hub.utils import RepositoryNotFoundError
4
+ import os
5
+
6
+
7
+ def create_interface():
8
+ """Create the Gradio interface with OAuth login for Apple Health Landing Zone."""
9
+
10
+ with gr.Blocks(title="Apple Health Landing Zone") as demo:
11
+ gr.Markdown("# Apple Health Landing Zone")
12
+ gr.Markdown("Login with your Hugging Face account to create a private dataset for your Apple Health export and an MCP server space.")
13
+
14
+ # OAuth login
15
+ gr.LoginButton()
16
+
17
+ # User info display
18
+ user_info = gr.Markdown("")
19
+
20
+ # Upload section (initially hidden)
21
+ with gr.Column(visible=False) as upload_section:
22
+ gr.Markdown("### Upload Apple Health Export")
23
+ gr.Markdown("Upload your export.xml file from Apple Health. This will create:")
24
+ gr.Markdown("1. A private dataset to store your health data")
25
+ gr.Markdown("2. A private space with an MCP server to query your data")
26
+
27
+ file_input = gr.File(
28
+ label="Apple Health export.xml",
29
+ file_types=[".xml"],
30
+ type="filepath"
31
+ )
32
+
33
+ space_name_input = gr.Textbox(
34
+ label="Project Name",
35
+ placeholder="my-health-data",
36
+ info="Enter a name for your health data project (lowercase, no spaces)"
37
+ )
38
+
39
+ create_btn = gr.Button("Create Landing Zone", variant="primary")
40
+ create_status = gr.Markdown("")
41
+
42
+ def create_health_landing_zone(file_path: str, project_name: str, oauth_token: gr.OAuthToken | None) -> str:
43
+ """Create private dataset and MCP server space for Apple Health data."""
44
+ if not oauth_token:
45
+ return "❌ Please login first!"
46
+
47
+ if not file_path:
48
+ return "❌ Please upload your export.xml file!"
49
+
50
+ if not project_name:
51
+ return "❌ Please enter a project name!"
52
+
53
+ try:
54
+ # Use the OAuth token
55
+ token = oauth_token.token
56
+ if not token:
57
+ return "❌ No access token found. Please login again."
58
+
59
+ api = HfApi(token=token)
60
+
61
+ # Get the current user's username
62
+ user_info = api.whoami()
63
+ username = user_info["name"]
64
+
65
+ # Create dataset repository
66
+ dataset_repo_id = f"{username}/{project_name}-data"
67
+ space_repo_id = f"{username}/{project_name}-mcp"
68
+
69
+ # Check if repositories already exist
70
+ try:
71
+ api.repo_info(dataset_repo_id, repo_type="dataset")
72
+ return f"❌ Dataset '{dataset_repo_id}' already exists!"
73
+ except RepositoryNotFoundError:
74
+ pass
75
+
76
+ try:
77
+ api.repo_info(space_repo_id, repo_type="space")
78
+ return f"❌ Space '{space_repo_id}' already exists!"
79
+ except RepositoryNotFoundError:
80
+ pass
81
+
82
+ # Create the private dataset
83
+ dataset_url = create_repo(
84
+ repo_id=dataset_repo_id,
85
+ repo_type="dataset",
86
+ private=True,
87
+ token=token
88
+ )
89
+
90
+ # Upload the export.xml file
91
+ api.upload_file(
92
+ path_or_fileobj=file_path,
93
+ path_in_repo="export.xml",
94
+ repo_id=dataset_repo_id,
95
+ repo_type="dataset",
96
+ token=token
97
+ )
98
+
99
+ # Create README for dataset
100
+ dataset_readme = f"""# Apple Health Data
101
+
102
+ This is a private dataset containing Apple Health export data for {username}.
103
+
104
+ ## Files
105
+ - `export.xml`: The Apple Health export file
106
+
107
+ ## Associated MCP Server
108
+ - Space: [{space_repo_id}](https://huggingface.co/spaces/{space_repo_id})
109
+
110
+ ## Privacy
111
+ This dataset is private and contains personal health information. Do not share access with others.
112
+ """
113
+
114
+ api.upload_file(
115
+ path_or_fileobj=dataset_readme.encode(),
116
+ path_in_repo="README.md",
117
+ repo_id=dataset_repo_id,
118
+ repo_type="dataset",
119
+ token=token
120
+ )
121
+
122
+ # Create the MCP server space
123
+ space_url = create_repo(
124
+ repo_id=space_repo_id,
125
+ repo_type="space",
126
+ space_sdk="gradio",
127
+ private=True,
128
+ token=token
129
+ )
130
+
131
+ # Create MCP server app.py
132
+ mcp_app_content = f'''import gradio as gr
133
+ from huggingface_hub import hf_hub_download
134
+ import xml.etree.ElementTree as ET
135
+ import pandas as pd
136
+ from datetime import datetime
137
+ import json
138
+
139
+ # Download the health data
140
+ DATA_REPO = "{dataset_repo_id}"
141
+
142
+ def load_health_data():
143
+ """Load and parse the Apple Health export.xml file."""
144
+ try:
145
+ # Download the export.xml file from the dataset
146
+ file_path = hf_hub_download(
147
+ repo_id=DATA_REPO,
148
+ filename="export.xml",
149
+ repo_type="dataset",
150
+ use_auth_token=True
151
+ )
152
+
153
+ # Parse the XML file
154
+ tree = ET.parse(file_path)
155
+ root = tree.getroot()
156
+
157
+ # Extract records
158
+ records = []
159
+ for record in root.findall('.//Record'):
160
+ records.append(record.attrib)
161
+
162
+ return pd.DataFrame(records)
163
+ except Exception as e:
164
+ return None
165
+
166
+ # Load data on startup
167
+ health_df = load_health_data()
168
+
169
+ def query_health_data(query_type, start_date=None, end_date=None):
170
+ """Query the health data based on user input."""
171
+ if health_df is None:
172
+ return "Error: Could not load health data."
173
+
174
+ df = health_df.copy()
175
+
176
+ # Filter by date if provided
177
+ if start_date:
178
+ df = df[df['startDate'] >= start_date]
179
+ if end_date:
180
+ df = df[df['endDate'] <= end_date]
181
+
182
+ if query_type == "summary":
183
+ # Get summary statistics
184
+ summary = {{
185
+ "Total Records": len(df),
186
+ "Record Types": df['type'].value_counts().to_dict() if 'type' in df.columns else {{}},
187
+ "Date Range": f"{{df['startDate'].min()}} to {{df['endDate'].max()}}" if 'startDate' in df.columns else "N/A"
188
+ }}
189
+ return json.dumps(summary, indent=2)
190
+
191
+ elif query_type == "recent":
192
+ # Get recent records
193
+ if 'startDate' in df.columns:
194
+ recent = df.nlargest(10, 'startDate')[['type', 'value', 'startDate', 'unit']].to_dict('records')
195
+ return json.dumps(recent, indent=2)
196
+ return "No date information available"
197
+
198
+ else:
199
+ return "Invalid query type"
200
+
201
+ # MCP Server Interface
202
+ with gr.Blocks(title="Apple Health MCP Server") as demo:
203
+ gr.Markdown("# Apple Health MCP Server")
204
+ gr.Markdown(f"This is an MCP server for querying Apple Health data from dataset: `{{DATA_REPO}}`")
205
+
206
+ with gr.Tab("Query Interface"):
207
+ query_type = gr.Dropdown(
208
+ choices=["summary", "recent"],
209
+ value="summary",
210
+ label="Query Type"
211
+ )
212
+
213
+ with gr.Row():
214
+ start_date = gr.Textbox(label="Start Date (YYYY-MM-DD)", placeholder="2024-01-01")
215
+ end_date = gr.Textbox(label="End Date (YYYY-MM-DD)", placeholder="2024-12-31")
216
+
217
+ query_btn = gr.Button("Run Query", variant="primary")
218
+ output = gr.Code(language="json", label="Query Results")
219
+
220
+ query_btn.click(
221
+ fn=query_health_data,
222
+ inputs=[query_type, start_date, end_date],
223
+ outputs=output
224
+ )
225
+
226
+ with gr.Tab("MCP Endpoint"):
227
+ gr.Markdown("""
228
+ ## MCP Server Endpoint
229
+
230
+ This space can be used as an MCP server with the following configuration:
231
+
232
+ ```json
233
+ {{
234
+ "mcpServers": {{
235
+ "apple-health": {{
236
+ "command": "npx",
237
+ "args": [
238
+ "-y",
239
+ "@modelcontextprotocol/server-huggingface",
240
+ "{space_repo_id}",
241
+ "--use-auth"
242
+ ]
243
+ }}
244
+ }}
245
+ }}
246
+ ```
247
+
248
+ Add this to your Claude Desktop configuration to query your Apple Health data through Claude.
249
+ """)
250
+
251
+ if __name__ == "__main__":
252
+ demo.launch()
253
+ '''
254
+
255
+ api.upload_file(
256
+ path_or_fileobj=mcp_app_content.encode(),
257
+ path_in_repo="app.py",
258
+ repo_id=space_repo_id,
259
+ repo_type="space",
260
+ token=token
261
+ )
262
+
263
+ # Create requirements.txt for the space
264
+ requirements_content = """gradio>=5.34.0
265
+ huggingface-hub>=0.20.0
266
+ pandas>=2.0.0
267
+ """
268
+
269
+ api.upload_file(
270
+ path_or_fileobj=requirements_content.encode(),
271
+ path_in_repo="requirements.txt",
272
+ repo_id=space_repo_id,
273
+ repo_type="space",
274
+ token=token
275
+ )
276
+
277
+ return f"""✅ Successfully created Apple Health Landing Zone!
278
+
279
+ **Private Dataset:** [{dataset_repo_id}]({dataset_url})
280
+ - Your export.xml file has been securely uploaded
281
+
282
+ **MCP Server Space:** [{space_repo_id}]({space_url})
283
+ - Query interface for your health data
284
+ - MCP endpoint configuration included
285
+
286
+ Both repositories are private and only accessible by you."""
287
+
288
+ except Exception as e:
289
+ return f"❌ Error creating landing zone: {str(e)}"
290
+
291
+ def update_ui(profile: gr.OAuthProfile | None) -> tuple:
292
+ """Update UI based on login status."""
293
+ if profile:
294
+ username = profile.username
295
+ return (
296
+ f"✅ Logged in as **{username}**",
297
+ gr.update(visible=True)
298
+ )
299
+ else:
300
+ return (
301
+ "",
302
+ gr.update(visible=False)
303
+ )
304
+
305
+ # Update UI when login state changes
306
+ demo.load(update_ui, inputs=None, outputs=[user_info, upload_section])
307
+
308
+ # Create landing zone button click
309
+ create_btn.click(
310
+ fn=create_health_landing_zone,
311
+ inputs=[file_input, space_name_input],
312
+ outputs=[create_status]
313
+ )
314
+
315
+ return demo
316
+
317
+
318
+ def main():
319
+ # Check if running in Hugging Face Spaces
320
+ if os.getenv("SPACE_ID"):
321
+ # Running in Spaces, launch with appropriate settings
322
+ demo = create_interface()
323
+ demo.launch()
324
+ else:
325
+ # Running locally, note that OAuth won't work
326
+ print("Note: OAuth login only works when deployed to Hugging Face Spaces.")
327
+ print("To test locally, deploy this as a Space with hf_oauth: true in README.md")
328
+ demo = create_interface()
329
+ demo.launch()
330
+
331
+
332
+ if __name__ == "__main__":
333
+ main()
pyproject.toml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "apple-health-landing-zone"
3
+ version = "0.1.0"
4
+ description = "Apple Health export.xml landing zone with MCP server creation"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "gradio>=5.34.0",
9
+ "huggingface-hub>=0.33.0",
10
+ ]
11
+
12
+ [dependency-groups]
13
+ dev = [
14
+ "huggingface-hub[cli]>=0.33.0",
15
+ "mypy>=1.16.0",
16
+ "pytest>=8.4.0",
17
+ "ruff>=0.11.13",
18
+ ]
uv.lock ADDED
The diff for this file is too large to render. See raw diff