Spaces:
Running
Running
File size: 19,430 Bytes
690cf25 4a1af2e 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 ea6bf79 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 ea6bf79 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 ea1a78f e50b8c7 ea1a78f 4a1af2e ea1a78f 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 ea6bf79 b2b9458 ea6bf79 b2b9458 ea6bf79 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 44c2693 b2b9458 44c2693 b2b9458 44c2693 b2b9458 ea1a78f e50b8c7 4a1af2e ea1a78f b2b9458 44c2693 b2b9458 44c2693 b2b9458 ea6bf79 b2b9458 ea6bf79 b2b9458 ea6bf79 b2b9458 44c2693 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 44c2693 b2b9458 44c2693 b2b9458 44c2693 b2b9458 44c2693 b2b9458 44c2693 b2b9458 44c2693 b2b9458 44c2693 b2b9458 44c2693 b2b9458 44c2693 b2b9458 44c2693 4a1af2e b2b9458 690cf25 b2b9458 44c2693 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 690cf25 b2b9458 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 |
import streamlit as st
import streamlit.components.v1 as components
import hashlib
import urllib.parse
from datetime import datetime
import pytz
import pandas as pd
import json
# --- Constants ---
MELBOURNE_TIMEZONE = 'Australia/Melbourne'
# --- Custom CSS for simplified UI ---
def load_css():
st.markdown("""
<style>
.main-header {
padding: 2rem;
text-align: center;
margin-bottom: 2rem;
}
.citation-output {
background: #f8f8f8;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 1rem;
margin: 1rem 0;
font-family: 'Courier New', monospace;
}
.copy-button {
background: #e0e0e0;
color: black;
border: none;
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
font-size: 0.9rem;
margin-top: 0.5rem;
}
.copy-button:hover {
background: #d0d0d0;
}
.warning-box {
background: #f8f8f8;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 1rem;
margin: 1rem 0;
}
.success-box {
background: #f8f8f8;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 1rem;
margin: 1rem 0;
}
.info-card {
background: white;
border-radius: 4px;
padding: 1.5rem;
margin: 1rem 0;
border-left: 1px solid #e0e0e0;
}
.footer {
text-align: center;
padding: 2rem;
margin-top: 2rem;
border-top: 1px solid #e0e0e0;
font-size: 0.9rem;
}
.hash-display {
background: #f8f8f8;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 1rem;
font-family: 'Courier New', monospace;
font-size: 0.85rem;
word-break: break-all;
margin: 0.5rem 0;
}
.tab-content {
padding: 2rem 0;
}
.datetime-display {
background: #f8f8f8;
border-radius: 4px;
padding: 0.8rem;
margin: 0.5rem 0;
border-left: 1px solid #e0e0e0;
}
.verification-table {
margin: 1rem 0;
border-radius: 4px;
overflow: hidden;
}
</style>
""", unsafe_allow_html=True)
# --- Helper Functions ---
def generate_citation_hash(author, year, url, fragment_text, cited_text, username, task_name, current_date, current_time):
data = f"{author}, {year} | {url} | {fragment_text} | {cited_text} | {username} | {task_name} | {current_date} | {current_time}"
return hashlib.sha256(data.encode('utf-8')).hexdigest()
def format_citation_html(url, fragment_text, author, year, scc_hash):
encoded_fragment = urllib.parse.quote(fragment_text)
full_url = f"{url}#:~:text={encoded_fragment}"
return f'<a href="{full_url}" data-hash="{scc_hash}">{author}, {year}</a>'
def check_for_fragment(url):
return '#:~:text=' in url
def copy_to_clipboard_js(text, button_id):
"""Generate JavaScript for copying text to clipboard"""
return f"""
<script>
function copyToClipboard_{button_id}() {{
navigator.clipboard.writeText(`{text}`).then(function() {{
document.getElementById('copy_status_{button_id}').innerHTML = 'Copied!';
setTimeout(function() {{
document.getElementById('copy_status_{button_id}').innerHTML = '';
}}, 2000);
}}, function(err) {{
document.getElementById('copy_status_{button_id}').innerHTML = 'Copy failed';
console.error('Could not copy text: ', err);
}});
}}
</script>
<button onclick="copyToClipboard_{button_id}()" class="copy-button">Copy to Clipboard</button>
<span id="copy_status_{button_id}" style="margin-left: 10px; font-weight: bold;"></span>
"""
# --- Live Clock JavaScript ---
def live_clock():
return """
<div class="datetime-display">
<span id="live_datetime"></span>
</div>
<script>
function updateClock() {
const options = {
timeZone: 'Australia/Melbourne',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
};
const formatter = new Intl.DateTimeFormat('en-AU', options);
const now = new Date();
const parts = formatter.formatToParts(now);
const date = `${parts[4].value}-${parts[2].value}-${parts[0].value}`;
const time = `${parts[6].value}:${parts[8].value}:${parts[10].value}`;
const datetimeElement = document.getElementById('live_datetime');
if (datetimeElement) {
datetimeElement.innerText = `${date} ${time}`;
}
}
updateClock();
setInterval(updateClock, 1000);
</script>
"""
# --- Streamlit App ---
st.set_page_config(layout="wide", page_title="Smart Context Citation Tool")
# Load custom CSS
load_css()
# Main header
st.markdown("""
<div class="main-header">
<h1>Smart Context Citation (SCC) Tool</h1>
<p>Next-generation digital referencing system for the age of Generative AI</p>
</div>
""", unsafe_allow_html=True)
# Expandable section for About and Example
with st.expander("About SCC and Example Citation"):
st.markdown("""
<div class="info-card">
<h3>About SCC</h3>
The Smart Context Citation (SCC) style is a next-generation digital referencing system designed for the age of Generative AI. It embeds citation context directly in the document, uses cryptographic hash signatures for integrity, and eliminates traditional reference lists.
<strong>Purpose:</strong> Transparency, integrity, and digital fluency in citations.
<strong>Structure:</strong>
- Inline general author name and date style citation
- Hyperlinked URL with text fragment (#:~:text=)
- SHA-256 hash for verification
<strong>Benefits:</strong> Enhances fairness, integrates with source contexts, promotes digital fluency, prevents fabrication, and eliminates traditional reference lists.
<strong>Technical Legitimacy:</strong> Referencing the <a href="https://wicg.github.io/scroll-to-text-fragment/" target="_blank">Text Fragments WICG specification</a> for technical legitimacy.
</div>
<div class="info-card">
<h3>Example Citation</h3>
<strong>Input:</strong><br>
- Author: <code>Abuseif et al.</code><br>
- Year: <code>2025</code><br>
- URL: <code>https://www.sciencedirect.com/science/article/pii/S2772411523000046</code><br>
- Text: <code>A proposed design framework for green roof settings in general and trees on buildings</code><br>
<strong>Output (HTML):</strong><br>
<div class="hash-display">
<a href="https://www.sciencedirect.com/science/article/pii/S2772411523000046#:~:text=A%20proposed%20design%20framework%20for%20green%20roof%20settings%20in%20general%20and%20trees%20on%20buildings" data-hash="[GENERATED_HASH]">Abuseif et al., 2025</a>
</div>
</div>
""", unsafe_allow_html=True)
tabs = st.tabs(["Citation Generator", "Verify Citation"])
with tabs[0]:
st.markdown('<div class="tab-content">', unsafe_allow_html=True)
st.header("Generate New Citation")
# User Information Section
st.subheader("User Information")
col1, col2 = st.columns(2)
with col1:
username = st.text_input("Username", help="Your username for tracking purposes", placeholder="e.g., john_doe")
with col2:
task_name = st.text_input("Task Name", help="The name of the task or project", placeholder="e.g., Literature Review Assignment")
# Citation Info Section
st.subheader("Citation Info")
col3, col4 = st.columns(2)
with col3:
author_name = st.text_input("Author(s) Name", help="The author(s) of the source", placeholder="e.g., Smith or Smith et al.")
with col4:
publication_year = st.text_input("Publication Year", help="The year of publication", placeholder="e.g., 2023")
col5, col6 = st.columns(2)
with col5:
source_url = st.text_input("Source URL", help="The full URL of the source", placeholder="https://example.com/article")
with col6:
annotated_text = st.text_input("Annotated Text", help="The text quoted or paraphrased from the source", placeholder="e.g., Thermal comfort thresholds...")
# Live date and time display
st.markdown("### Current Date and Time")
components.html(live_clock(), height=50)
# Get current date and time in Melbourne timezone for hash generation
melbourne_tz = pytz.timezone(MELBOURNE_TIMEZONE)
current_datetime_melbourne = datetime.now(melbourne_tz)
current_date = current_datetime_melbourne.strftime("%Y-%m-%d")
current_time = current_datetime_melbourne.strftime("%H:%M:%S")
generate_button = st.button("Generate Citation", type="primary", use_container_width=True)
if generate_button:
if not all([username, task_name, author_name, publication_year, source_url, annotated_text]):
st.error("Please fill in all fields before generating a citation.")
elif check_for_fragment(source_url):
st.markdown("""
<div class="warning-box">
<strong>Warning:</strong> It seems like your URL already contains a text fragment (<code>#:~:text=</code>).
This suggests you may have used AI assistance in generating this link. Please go back to the original source,
read the context carefully, and copy the source link again without any existing fragment.
</div>
""", unsafe_allow_html=True)
else:
scc_hash = generate_citation_hash(author_name, publication_year, source_url, annotated_text, annotated_text, username, task_name, current_date, current_time)
st.markdown("## Generated Citations")
col_html1, col_html2 = st.columns(2)
# HTML Citation - Start of Text
with col_html1:
st.markdown("### HTML Citation (Start of Text)")
html_citation_start = f'"{annotated_text}" (<a href="{source_url}#:~:text={urllib.parse.quote(annotated_text)}" data-hash="{scc_hash}">{author_name}, {publication_year}</a>)'
st.markdown('<div class="citation-output">', unsafe_allow_html=True)
st.code(html_citation_start, language='html')
st.markdown(copy_to_clipboard_js(html_citation_start.replace('`', '\\`'), "html_start"), unsafe_allow_html=True)
st.markdown('</div>', unsafe_allow_html=True)
# HTML Citation - End of Text
with col_html2:
st.markdown("### HTML Citation (End of Text)")
html_citation_end = f'(<a href="{source_url}#:~:text={urllib.parse.quote(annotated_text)}" data-hash="{scc_hash}">{author_name}, {publication_year}</a>) "{annotated_text}"'
st.markdown('<div class="citation-output">', unsafe_allow_html=True)
st.code(html_citation_end, language='html')
st.markdown(copy_to_clipboard_js(html_citation_end.replace('`', '\\`'), "html_end"), unsafe_allow_html=True)
st.markdown('</div>', unsafe_allow_html=True)
# Citation Hash Details
st.markdown("### Citation Hash Details (for Verification)")
hash_details = {
"author": author_name,
"year": publication_year,
"url": source_url,
"fragment_text": annotated_text,
"cited_text": annotated_text,
"username": username,
"task_name": task_name,
"date": current_date,
"time": current_time,
"hash": scc_hash
}
st.markdown('<div class="hash-display">', unsafe_allow_html=True)
st.json(hash_details)
st.markdown('</div>', unsafe_allow_html=True)
# Instructions section
st.markdown("## Instructions for Copying to Word")
st.markdown("""
<div class="info-card">
<strong>To use the generated HTML citation in Microsoft Word:</strong><br><br>
1. Copy the desired HTML citation (Start or End of Text) using the 'Copy to Clipboard' button<br>
2. In Word, go to the 'Insert' tab<br>
3. Click on 'Object' → 'Text from File...'<br>
4. Select 'HTML Document' from the file type dropdown<br>
5. Paste the copied HTML into a new text file (e.g., using Notepad) and save it with a <code>.html</code> extension<br>
6. Select this <code>.html</code> file in the 'Text from File...' dialog<br><br>
<strong>Alternative method:</strong> You might be able to paste directly into Word and then right-click and choose 'Keep Source Formatting' or 'Merge Formatting' if available, but the 'Text from File' method is more reliable for preserving hyperlinks and data attributes.
</div>
""", unsafe_allow_html=True)
st.markdown("## Guidance on Verifying Citations")
st.markdown("""
<div class="info-card">
To verify a citation, you can recompute the hash using the original input data and compare it to the embedded hash.
The <strong>'Verify Citation'</strong> tab allows you to do this easily.
</div>
""", unsafe_allow_html=True)
st.markdown('</div>', unsafe_allow_html=True)
with tabs[1]:
st.markdown('<div class="tab-content">', unsafe_allow_html=True)
st.header("Verify Citation")
st.markdown("""
<div class="info-card">
Enter the citation details below to recompute and verify the hash. This ensures the citation hasn't been tampered with or fabricated.
</div>
""", unsafe_allow_html=True)
# Initialize session state for storing verified hashes
if 'verified_hashes' not in st.session_state:
st.session_state.verified_hashes = []
# User Information Section
st.subheader("User Information")
col1, col2 = st.columns(2)
with col1:
verify_username = st.text_input("Username (for verification)", placeholder="e.g., john_doe")
with col2:
verify_task_name = st.text_input("Task Name (for verification)", placeholder="e.g., Literature Review Assignment")
# Citation Info Section
st.subheader("Citation Info")
col3, col4 = st.columns(2)
with col3:
verify_author_name = st.text_input("Author(s) Name (for verification)", placeholder="e.g., Smith or Smith et al.")
with col4:
verify_publication_year = st.text_input("Publication Year (for verification)", placeholder="e.g., 2023")
col5, col6 = st.columns(2)
with col5:
verify_source_url = st.text_input("Source URL (for verification)", placeholder="https://example.com/article")
with col6:
verify_annotated_text = st.text_input("Annotated Text (for verification)", placeholder="e.g., Thermal comfort thresholds...")
col7, col8 = st.columns(2)
with col7:
verify_date = st.text_input("Date (YYYY-MM-DD) (for verification)", placeholder="e.g., 2025-01-08")
with col8:
verify_time = st.text_input("Time (HH:MM:SS) (for verification)", placeholder="e.g., 14:30:25")
expected_hash = st.text_input("Expected Hash (from the citation)", placeholder="Enter the full hash from the citation")
verify_button = st.button("Verify Hash", type="primary", use_container_width=True)
if verify_button:
if not all([verify_username, verify_task_name, verify_author_name, verify_publication_year,
verify_source_url, verify_annotated_text, verify_date, verify_time, expected_hash]):
st.error("Please fill in all fields before verifying the hash.")
else:
recomputed_hash = generate_citation_hash(
verify_author_name, verify_publication_year, verify_source_url,
verify_annotated_text, verify_annotated_text, verify_username, verify_task_name,
verify_date, verify_time
)
if recomputed_hash == expected_hash:
st.markdown("""
<div class="success-box">
<strong>Hash verified successfully!</strong> The citation is authentic and hasn't been tampered with.
</div>
""", unsafe_allow_html=True)
st.session_state.verified_hashes.append({
"Author": verify_author_name,
"Year": verify_publication_year,
"URL": verify_source_url,
"Fragment text": verify_annotated_text,
"Outlined text": verify_annotated_text,
"Username": verify_username,
"Task name": verify_task_name,
"Date": verify_date,
"Time": verify_time,
"Original Hash": expected_hash,
"Recomputed Hash": recomputed_hash,
"Status": "Verified"
})
else:
st.markdown("""
<div class="warning-box">
<strong>Hash verification failed!</strong> The citation may have been altered or is not authentic.
</div>
""", unsafe_allow_html=True)
st.session_state.verified_hashes.append({
"Author": verify_author_name,
"Year": verify_publication_year,
"URL": verify_source_url,
"Fragment text": verify_annotated_text,
"Cited text": verify_annotated_text,
"Username": verify_username,
"Task name": verify_task_name,
"Date": verify_date,
"Time": verify_time,
"Original Hash": expected_hash,
"Recomputed Hash": recomputed_hash,
"Status": "Failed"
})
if st.session_state.verified_hashes:
st.markdown("## Verification History")
df = pd.DataFrame(st.session_state.verified_hashes)
st.markdown('<div class="verification-table">', unsafe_allow_html=True)
st.dataframe(df, use_container_width=True)
st.markdown('</div>', unsafe_allow_html=True)
# Download as CSV
@st.cache_data
def convert_df_to_csv(df):
return df.to_csv(index=False).encode('utf-8')
csv = convert_df_to_csv(df)
st.download_button(
label="Download Verification History as CSV",
data=csv,
file_name="scc_verification_history.csv",
mime="text/csv",
use_container_width=True
)
# Clear history button
if st.button("Clear Verification History", type="secondary"):
st.session_state.verified_hashes = []
st.experimental_rerun()
st.markdown('</div>', unsafe_allow_html=True)
# Footer
st.markdown("""
<div class="footer">
Developed by: Dr Majed Abuseif<br>
School of Architecture and Built Environment<br>
Deakin University<br>
© 2025
</div>
""", unsafe_allow_html=True) |