|
import json |
|
import gradio as gr |
|
from fastapi import FastAPI, HTTPException |
|
from pydantic import BaseModel |
|
from fastapi.responses import HTMLResponse |
|
|
|
|
|
lists = {} |
|
|
|
|
|
class DataRequest(BaseModel): |
|
list_name: str |
|
items: list |
|
|
|
|
|
with gr.Blocks() as demo: |
|
|
|
gr.Markdown("# UniShare Data Viewer") |
|
output_text = gr.Textbox(label="Stored Data") |
|
|
|
def show_data(): |
|
return json.dumps(lists, indent=4) |
|
|
|
refresh_btn = gr.Button("Refresh Data") |
|
refresh_btn.click(fn=show_data, inputs=[], outputs=[output_text]) |
|
|
|
|
|
demo.load(fn=show_data, inputs=[], outputs=[output_text]) |
|
|
|
|
|
app = FastAPI() |
|
|
|
|
|
@app.post("/store_data") |
|
async def store_data(data: DataRequest): |
|
list_name = data.list_name |
|
items = data.items |
|
|
|
if list_name not in lists: |
|
lists[list_name] = [] |
|
|
|
lists[list_name].append(items) |
|
return {"message": f"Data stored successfully for {list_name}"} |
|
|
|
|
|
@app.get("/api/data") |
|
async def get_data(): |
|
return lists |
|
|
|
|
|
@app.get("/app", response_class=HTMLResponse) |
|
def read_root(): |
|
return """ |
|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8" /> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|
<title>UniShare</title> |
|
<style> |
|
body { |
|
font-family: Arial, sans-serif; |
|
margin: 0; |
|
padding: 0; |
|
background: #f0f2f5; |
|
} |
|
header { |
|
background: #3b82f6; |
|
color: white; |
|
padding: 1em; |
|
text-align: center; |
|
font-size: 1.8em; |
|
} |
|
#overview { |
|
display: grid; |
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); |
|
gap: 1em; |
|
padding: 1em; |
|
} |
|
.list-tile { |
|
background: white; |
|
padding: 1em; |
|
border-radius: 10px; |
|
box-shadow: 0 2px 5px rgba(0,0,0,0.1); |
|
cursor: pointer; |
|
transition: transform 0.2s; |
|
} |
|
.list-tile:hover { |
|
transform: scale(1.02); |
|
} |
|
#list-view { |
|
display: none; |
|
padding: 1em; |
|
} |
|
.back-button { |
|
background: #3b82f6; |
|
color: white; |
|
padding: 0.5em 1em; |
|
border: none; |
|
border-radius: 5px; |
|
cursor: pointer; |
|
margin-bottom: 1em; |
|
} |
|
.card { |
|
background: white; |
|
padding: 1em; |
|
border-radius: 10px; |
|
box-shadow: 0 2px 5px rgba(0,0,0,0.1); |
|
margin-bottom: 1em; |
|
} |
|
.submenu { |
|
margin-top: 1em; |
|
} |
|
.submenu h4 { |
|
margin: 0.5em 0; |
|
} |
|
.grid { |
|
display: grid; |
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); |
|
gap: 1em; |
|
} |
|
.item { |
|
background: #f9f9f9; |
|
padding: 1em; |
|
border-radius: 10px; |
|
box-shadow: 0 1px 3px rgba(0,0,0,0.05); |
|
} |
|
.item img { |
|
max-width: 100%; |
|
max-height: 150px; |
|
object-fit: cover; |
|
border-radius: 5px; |
|
margin-bottom: 0.5em; |
|
} |
|
.item a { |
|
display: inline-block; |
|
margin-top: 0.5em; |
|
text-decoration: none; |
|
color: #3b82f6; |
|
} |
|
#toast { |
|
position: fixed; |
|
bottom: 20px; |
|
right: 20px; |
|
background: #333; |
|
color: white; |
|
padding: 1em; |
|
border-radius: 5px; |
|
display: none; |
|
z-index: 1000; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<header>UniShare</header> |
|
<div id="overview"></div> |
|
<div id="list-view"> |
|
<button class="back-button" onclick="backToOverview()">← Back to Overview</button> |
|
<div id="content"></div> |
|
</div> |
|
<div id="toast"></div> |
|
|
|
<script> |
|
const overviewDiv = document.getElementById('overview'); |
|
const contentDiv = document.getElementById('content'); |
|
const listViewDiv = document.getElementById('list-view'); |
|
const toastDiv = document.getElementById('toast'); |
|
|
|
const lists = {} |
|
|
|
function showToast(message) { |
|
toastDiv.textContent = message; |
|
toastDiv.style.display = 'block'; |
|
setTimeout(() => { |
|
toastDiv.style.display = 'none'; |
|
}, 3000); |
|
} |
|
|
|
function backToOverview() { |
|
listViewDiv.style.display = 'none'; |
|
overviewDiv.style.display = 'grid'; |
|
} |
|
|
|
function showList(name) { |
|
const sections = lists[name]; |
|
contentDiv.innerHTML = ''; |
|
|
|
sections.forEach(section => { |
|
const card = document.createElement('div'); |
|
card.className = 'card'; |
|
const title = document.createElement('h3'); |
|
title.textContent = section.sectionTitle; |
|
card.appendChild(title); |
|
|
|
const grid = document.createElement('div'); |
|
grid.className = 'grid'; |
|
|
|
section.items.forEach(item => { |
|
const div = document.createElement('div'); |
|
div.className = 'item'; |
|
let content = `<strong>${item.title}</strong><br>`; |
|
if (item.link.match(/\.(jpeg|jpg|gif|png)$/)) { |
|
content += `<img src="${item.link}" alt="${item.title}" />`; |
|
} |
|
content += `<a href="${item.link}" download>Download</a>`; |
|
div.innerHTML = content; |
|
grid.appendChild(div); |
|
}); |
|
|
|
card.appendChild(grid); |
|
contentDiv.appendChild(card); |
|
}); |
|
|
|
overviewDiv.style.display = 'none'; |
|
listViewDiv.style.display = 'block'; |
|
} |
|
|
|
// Fetch data and populate the lists dynamically |
|
fetch('/api/data') |
|
.then(response => response.json()) |
|
.then(data => { |
|
if (Object.keys(data).length > 0) { |
|
Object.keys(data).forEach((listName, index) => { |
|
const list = data[listName]; |
|
const tile = document.createElement('div'); |
|
tile.className = 'list-tile'; |
|
tile.innerHTML = `<strong>${listName}</strong><br>${list.length} sections`; |
|
tile.onclick = () => showList(listName); |
|
overviewDiv.appendChild(tile); |
|
showToast(`Loaded ${listName}`); |
|
}); |
|
} else { |
|
overviewDiv.innerHTML = '<p>No lists found</p>'; |
|
} |
|
}); |
|
</script> |
|
</body> |
|
</html> |
|
""" |
|
|
|
|
|
app = gr.mount_gradio_app(app, demo, path="/") |