Spaces:
Sleeping
Sleeping
import streamlit as st | |
import networkx as nx | |
import matplotlib.pyplot as plt | |
import textwrap | |
from io import BytesIO | |
import uuid | |
class RevisionApp: | |
def __init__(self): | |
self.initialize_session_state() | |
self.handle_user_selection() | |
self.create_ui() | |
def initialize_session_state(self): | |
if 'users' not in st.session_state: | |
st.session_state.users = {} | |
if 'current_user' not in st.session_state: | |
st.session_state.current_user = None | |
if 'user_sessions' not in st.session_state: | |
st.session_state.user_sessions = {} | |
def handle_user_selection(self): | |
st.sidebar.title("User Selection") | |
user_name = st.sidebar.text_input("Enter your name:") | |
if st.sidebar.button("Set User"): | |
if user_name: | |
# Check if the username is already in use in this session | |
if user_name in st.session_state.user_sessions: | |
st.sidebar.error(f"The name '{user_name}' is already in use. Please choose a different name.") | |
else: | |
# Generate a unique session ID for this user | |
session_id = str(uuid.uuid4()) | |
st.session_state.current_user = f"{user_name}_{session_id}" | |
st.session_state.user_sessions[user_name] = session_id | |
if st.session_state.current_user not in st.session_state.users: | |
st.session_state.users[st.session_state.current_user] = {} | |
st.success(f"User set to: {user_name}") | |
st.experimental_rerun() | |
def create_ui(self): | |
if st.session_state.current_user: | |
display_name = st.session_state.current_user.split('_')[0] # Extract the user's name without the session ID | |
st.title(f"Concept Revision App - Welcome, {display_name}!") | |
# Navigation | |
page = st.sidebar.selectbox("Choose a page", ["Tree View", "Mind Map", "Search"]) | |
if page == "Tree View": | |
self.show_tree_view() | |
elif page == "Mind Map": | |
self.show_mind_map() | |
elif page == "Search": | |
self.show_search() | |
else: | |
st.title("Concept Revision App") | |
st.write("Please enter your name in the sidebar to begin.") | |
# The rest of the methods (show_tree_view, show_concept_details, show_mind_map, show_search, search_data, custom_tree_layout) | |
# remain the same as in the previous version, just ensure you're using st.session_state.current_user | |
# to access the correct user data in st.session_state.users | |
# Example of how to modify a method to use the new user identifier: | |
def show_tree_view(self): | |
st.header("Tree View") | |
# Input for new concept | |
new_key = st.text_input("Enter a new concept:") | |
if st.button("Add Concept"): | |
if new_key and new_key not in st.session_state.users[st.session_state.current_user]: | |
st.session_state.users[st.session_state.current_user][new_key] = {'next': [], 'text': []} | |
st.success(f"Added new concept: {new_key}") | |
st.experimental_rerun() | |
# Display concepts | |
user_data = st.session_state.users[st.session_state.current_user] | |
selected_concept = st.selectbox("Select a concept to view details:", | |
options=[""] + list(user_data.keys())) | |
if selected_concept: | |
self.show_concept_details(selected_concept) | |
# ... (other methods remain the same, just ensure you're using st.session_state.current_user consistently) | |
# def show_tree_view(self): | |
# st.header("Tree View") | |
# # Input for new concept | |
# new_key = st.text_input("Enter a new concept:") | |
# if st.button("Add Concept"): | |
# if new_key and new_key not in st.session_state.users[st.session_state.current_user]: | |
# st.session_state.users[st.session_state.current_user][new_key] = {'next': [], 'text': []} | |
# st.success(f"Added new concept: {new_key}") | |
# st.experimental_rerun() | |
# # Display concepts | |
# user_data = st.session_state.users[st.session_state.current_user] | |
# selected_concept = st.selectbox("Select a concept to view details:", | |
# options=[""] + list(user_data.keys())) | |
# if selected_concept: | |
# self.show_concept_details(selected_concept) | |
def show_concept_details(self, key): | |
user_data = st.session_state.users[st.session_state.current_user] | |
st.subheader(f"Concept: {key}") | |
# Display related concepts | |
st.write("Related Concepts:") | |
for next_item in user_data[key]['next']: | |
if st.button(f"Go to {next_item}", key=f"goto_{next_item}"): | |
self.show_concept_details(next_item) | |
return | |
# Add related concept | |
new_related = st.text_input(f"Add related concept to {key}:", key=f"related_{key}") | |
if st.button(f"Add related to {key}", key=f"add_related_{key}"): | |
if new_related and new_related not in user_data[key]['next']: | |
if new_related not in user_data: | |
user_data[new_related] = {'next': [], 'text': []} | |
user_data[key]['next'].append(new_related) | |
st.success(f"Added {new_related} as related to {key}") | |
st.experimental_rerun() | |
# Display information | |
st.write("Information:") | |
for i, text_item in enumerate(user_data[key]['text']): | |
st.text_area(f"Info {i+1}", value=text_item, key=f"info_{key}_{i}", height=100, disabled=True) | |
# Add information | |
new_info = st.text_area(f"Add information to {key}:", key=f"new_info_{key}") | |
if st.button(f"Add info to {key}", key=f"add_info_{key}"): | |
if new_info: | |
user_data[key]['text'].append(new_info) | |
st.success(f"Added new information to {key}") | |
st.experimental_rerun() | |
def show_mind_map(self): | |
st.header("Mind Map") | |
user_data = st.session_state.users[st.session_state.current_user] | |
G = nx.Graph() | |
for key, value in user_data.items(): | |
G.add_node(key) | |
for next_item in value['next']: | |
if next_item in user_data: | |
G.add_edge(key, next_item) | |
pos = self.custom_tree_layout(G) | |
plt.figure(figsize=(12, 8)) | |
nx.draw(G, pos, with_labels=False, node_color='lightblue', node_size=3000, alpha=0.8) | |
for node, (x, y) in pos.items(): | |
lines = textwrap.wrap(node, width=10) | |
plt.annotate('\n'.join(lines), (x, y), horizontalalignment='center', verticalalignment='center') | |
plt.axis('off') | |
buf = BytesIO() | |
plt.savefig(buf, format="png") | |
buf.seek(0) | |
st.image(buf, caption='Mind Map', use_column_width=True) | |
def show_search(self): | |
st.header("Search") | |
query = st.text_input("Enter search term:") | |
if st.button("Search"): | |
results = self.search_data(query) | |
if results: | |
for key in results: | |
with st.expander(f"Concept: {key}"): | |
st.write("Related Concepts:") | |
for related in st.session_state.users[st.session_state.current_user][key]['next']: | |
st.write(f"- {related}") | |
st.write("Information:") | |
for info in st.session_state.users[st.session_state.current_user][key]['text']: | |
st.write(f"- {info}") | |
else: | |
st.write("No results found.") | |
def search_data(self, query): | |
query = query.lower() | |
user_data = st.session_state.users[st.session_state.current_user] | |
results = set() | |
for key, value in user_data.items(): | |
if query in key.lower(): | |
results.add(key) | |
for next_item in value['next']: | |
if query in next_item.lower(): | |
results.add(key) | |
for text_item in value['text']: | |
if query in text_item.lower(): | |
results.add(key) | |
return list(results) | |
def custom_tree_layout(self, G): | |
if not G.nodes(): | |
return {} | |
def bfs_tree(root): | |
tree = nx.bfs_tree(G, root) | |
return tree | |
def assign_positions(tree, root): | |
pos = {} | |
level_width = {} | |
max_depth = 0 | |
def dfs(node, depth, order): | |
nonlocal max_depth | |
max_depth = max(max_depth, depth) | |
if depth not in level_width: | |
level_width[depth] = 0 | |
level_width[depth] += 1 | |
children = list(tree.successors(node)) | |
if not children: | |
pos[node] = (order, -depth) | |
return order + 1 | |
start = order | |
for child in children: | |
order = dfs(child, depth + 1, order) | |
pos[node] = (start + (order - start - 1) / 2, -depth) | |
return order | |
dfs(root, 0, 0) | |
# Normalize positions | |
max_width = max(level_width.values()) if level_width else 1 | |
for node in pos: | |
x, y = pos[node] | |
pos[node] = (x / max_width, y / max_depth if max_depth != 0 else 0) | |
return pos | |
# Handle disconnected components | |
components = list(nx.connected_components(G)) | |
if not components: | |
return {} | |
pos = {} | |
y_offset = 0 | |
for component in components: | |
subgraph = G.subgraph(component) | |
root = max(subgraph.nodes(), key=lambda n: subgraph.degree(n)) | |
tree = bfs_tree(root) | |
component_pos = assign_positions(tree, root) | |
# Adjust y-positions for each component | |
for node, (x, y) in component_pos.items(): | |
pos[node] = (x, y + y_offset) | |
y_offset -= 1.5 # Increase vertical separation between components | |
return pos | |
if __name__ == "__main__": | |
app = RevisionApp() | |