Spaces:
Running
Running
Upload 3 files
Browse files- README.md +109 -19
- app.py +480 -0
- requirements.txt +3 -2
README.md
CHANGED
@@ -1,19 +1,109 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Smart Context Citation (SCC) Tool
|
2 |
+
|
3 |
+
A Python-based tool designed for Hugging Face Spaces that automates the implementation of the Smart Context Citation (SCC) referencing style.
|
4 |
+
|
5 |
+
## Overview
|
6 |
+
|
7 |
+
The Smart Context Citation (SCC) style is a next-generation digital referencing system designed to address the evolving landscape of academic writing in the age of Generative AI (GenAI) and online information dependency. It redefines citation practices by embedding citation context directly in the document, eliminating the need for traditional reference lists, and using cryptographic hash signatures to ensure citation integrity.
|
8 |
+
|
9 |
+
## Features
|
10 |
+
|
11 |
+
### Citation Generator Tab
|
12 |
+
- Input fields for username, task name, author name, publication year, source URL, and cited text
|
13 |
+
- Automatic date and time display (Melbourne, Australia timezone)
|
14 |
+
- Generation of SCC-compliant citations in HTML and plaintext formats
|
15 |
+
- SHA-256 hash generation for citation integrity
|
16 |
+
- Copy-to-clipboard functionality for all outputs
|
17 |
+
- Warning system for URLs that already contain text fragments
|
18 |
+
- Comprehensive SCC style documentation and instructions
|
19 |
+
|
20 |
+
### Verify Citation Tab
|
21 |
+
- Hash verification system to check citation authenticity
|
22 |
+
- Table display of verification history
|
23 |
+
- Download verification history as CSV file
|
24 |
+
- Support for multiple hash verification
|
25 |
+
|
26 |
+
## SCC Style Benefits
|
27 |
+
|
28 |
+
- **Integrity**: Prevents citation manipulation or fabrication
|
29 |
+
- **Transparency**: Readers see exactly what was cited
|
30 |
+
- **Efficiency**: Eliminates duplication in reference lists
|
31 |
+
- **Digital Fluency**: Reflects real-world web-based research behavior
|
32 |
+
- **AI-Resistant**: Makes it difficult for GenAI tools to generate fake or superficial citations
|
33 |
+
|
34 |
+
## Installation and Setup
|
35 |
+
|
36 |
+
### For Hugging Face Spaces
|
37 |
+
|
38 |
+
1. Create a new Space on Hugging Face
|
39 |
+
2. Upload the following files:
|
40 |
+
- `app.py` (main application)
|
41 |
+
- `requirements.txt` (dependencies)
|
42 |
+
- `README.md` (this file)
|
43 |
+
3. Set the Space SDK to "Streamlit"
|
44 |
+
4. The app will automatically deploy
|
45 |
+
|
46 |
+
### For Local Development
|
47 |
+
|
48 |
+
1. Clone or download the repository
|
49 |
+
2. Install dependencies:
|
50 |
+
```bash
|
51 |
+
pip install -r requirements.txt
|
52 |
+
```
|
53 |
+
3. Run the application:
|
54 |
+
```bash
|
55 |
+
streamlit run app.py
|
56 |
+
```
|
57 |
+
|
58 |
+
## Usage Examples
|
59 |
+
|
60 |
+
### Example Citation Generation
|
61 |
+
|
62 |
+
**Input:**
|
63 |
+
- Author: `Abuseif et al.`
|
64 |
+
- Year: `2025`
|
65 |
+
- URL: `https://www.sciencedirect.com/science/article/pii/S2772411523000046`
|
66 |
+
- Text: `A proposed design framework for green roof settings in general and trees on buildings`
|
67 |
+
|
68 |
+
**Output (HTML):**
|
69 |
+
```html
|
70 |
+
<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>
|
71 |
+
```
|
72 |
+
|
73 |
+
**Output (Plaintext):**
|
74 |
+
```
|
75 |
+
(Abuseif et al., 2025) [hash: a78c45f91e2d...]
|
76 |
+
```
|
77 |
+
|
78 |
+
## Technical Details
|
79 |
+
|
80 |
+
### Hash Generation
|
81 |
+
The SHA-256 hash is generated from the concatenated string:
|
82 |
+
```
|
83 |
+
Author, Year | URL | Fragment text | Cited text | username | task name | date | time
|
84 |
+
```
|
85 |
+
|
86 |
+
### Text Fragment Support
|
87 |
+
The tool uses the Text Fragments WICG specification to create URLs that highlight specific text passages in modern browsers using the `#:~:text=` syntax.
|
88 |
+
|
89 |
+
## Dependencies
|
90 |
+
|
91 |
+
- `streamlit`: Web application framework
|
92 |
+
- `pytz`: Timezone handling for Melbourne, Australia timezone
|
93 |
+
- `pandas`: Data manipulation for verification history
|
94 |
+
|
95 |
+
## Creator
|
96 |
+
|
97 |
+
Developed by: Dr Majed Abuseif
|
98 |
+
School of Architecture and Built Environment
|
99 |
+
Deakin University
|
100 |
+
Β© 2025
|
101 |
+
|
102 |
+
## Technical Legitimacy
|
103 |
+
|
104 |
+
This tool references the [Text Fragments WICG specification](https://wicg.github.io/scroll-to-text-fragment/) to reinforce the technical legitimacy of the approach.
|
105 |
+
|
106 |
+
## License
|
107 |
+
|
108 |
+
Β© 2025 Dr Majed Abuseif, Deakin University. All rights reserved.
|
109 |
+
|
app.py
ADDED
@@ -0,0 +1,480 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import streamlit as st
|
3 |
+
import hashlib
|
4 |
+
import urllib.parse
|
5 |
+
from datetime import datetime
|
6 |
+
import pytz
|
7 |
+
import pandas as pd
|
8 |
+
import json
|
9 |
+
|
10 |
+
# --- Constants ---
|
11 |
+
MELBOURNE_TIMEZONE = 'Australia/Melbourne'
|
12 |
+
|
13 |
+
# --- Custom CSS for enhanced UI ---
|
14 |
+
def load_css():
|
15 |
+
st.markdown("""
|
16 |
+
<style>
|
17 |
+
.main-header {
|
18 |
+
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
|
19 |
+
padding: 2rem;
|
20 |
+
border-radius: 10px;
|
21 |
+
color: white;
|
22 |
+
text-align: center;
|
23 |
+
margin-bottom: 2rem;
|
24 |
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
25 |
+
}
|
26 |
+
|
27 |
+
.citation-output {
|
28 |
+
background: #f8f9fa;
|
29 |
+
border: 2px solid #e9ecef;
|
30 |
+
border-radius: 8px;
|
31 |
+
padding: 1rem;
|
32 |
+
margin: 1rem 0;
|
33 |
+
font-family: 'Courier New', monospace;
|
34 |
+
position: relative;
|
35 |
+
}
|
36 |
+
|
37 |
+
.copy-button {
|
38 |
+
background: #28a745;
|
39 |
+
color: white;
|
40 |
+
border: none;
|
41 |
+
padding: 0.5rem 1rem;
|
42 |
+
border-radius: 5px;
|
43 |
+
cursor: pointer;
|
44 |
+
font-size: 0.9rem;
|
45 |
+
margin-top: 0.5rem;
|
46 |
+
transition: background-color 0.3s;
|
47 |
+
}
|
48 |
+
|
49 |
+
.copy-button:hover {
|
50 |
+
background: #218838;
|
51 |
+
}
|
52 |
+
|
53 |
+
.warning-box {
|
54 |
+
background: #fff3cd;
|
55 |
+
border: 1px solid #ffeaa7;
|
56 |
+
border-radius: 8px;
|
57 |
+
padding: 1rem;
|
58 |
+
margin: 1rem 0;
|
59 |
+
color: #856404;
|
60 |
+
}
|
61 |
+
|
62 |
+
.success-box {
|
63 |
+
background: #d4edda;
|
64 |
+
border: 1px solid #c3e6cb;
|
65 |
+
border-radius: 8px;
|
66 |
+
padding: 1rem;
|
67 |
+
margin: 1rem 0;
|
68 |
+
color: #155724;
|
69 |
+
}
|
70 |
+
|
71 |
+
.info-card {
|
72 |
+
background: white;
|
73 |
+
border-radius: 10px;
|
74 |
+
padding: 1.5rem;
|
75 |
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
76 |
+
margin: 1rem 0;
|
77 |
+
border-left: 4px solid #667eea;
|
78 |
+
}
|
79 |
+
|
80 |
+
.creator-info {
|
81 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
82 |
+
color: white;
|
83 |
+
padding: 1rem;
|
84 |
+
border-radius: 8px;
|
85 |
+
text-align: center;
|
86 |
+
margin: 2rem 0;
|
87 |
+
font-size: 0.9rem;
|
88 |
+
}
|
89 |
+
|
90 |
+
.hash-display {
|
91 |
+
background: #f1f3f4;
|
92 |
+
border: 1px solid #dadce0;
|
93 |
+
border-radius: 6px;
|
94 |
+
padding: 1rem;
|
95 |
+
font-family: 'Courier New', monospace;
|
96 |
+
font-size: 0.85rem;
|
97 |
+
word-break: break-all;
|
98 |
+
margin: 0.5rem 0;
|
99 |
+
}
|
100 |
+
|
101 |
+
.tab-content {
|
102 |
+
padding: 2rem 0;
|
103 |
+
}
|
104 |
+
|
105 |
+
.form-section {
|
106 |
+
background: white;
|
107 |
+
padding: 2rem;
|
108 |
+
border-radius: 10px;
|
109 |
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
110 |
+
margin: 1rem 0;
|
111 |
+
}
|
112 |
+
|
113 |
+
.datetime-display {
|
114 |
+
background: #e3f2fd;
|
115 |
+
border-radius: 6px;
|
116 |
+
padding: 0.8rem;
|
117 |
+
margin: 0.5rem 0;
|
118 |
+
border-left: 4px solid #2196f3;
|
119 |
+
}
|
120 |
+
|
121 |
+
.verification-table {
|
122 |
+
margin: 1rem 0;
|
123 |
+
border-radius: 8px;
|
124 |
+
overflow: hidden;
|
125 |
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
126 |
+
}
|
127 |
+
</style>
|
128 |
+
""", unsafe_allow_html=True)
|
129 |
+
|
130 |
+
# --- Helper Functions ---
|
131 |
+
def generate_citation_hash(author, year, url, fragment_text, cited_text, username, task_name, current_date, current_time):
|
132 |
+
data = f"{author}, {year} | {url} | {fragment_text} | {cited_text} | {username} | {task_name} | {current_date} | {current_time}"
|
133 |
+
return hashlib.sha256(data.encode('utf-8')).hexdigest()
|
134 |
+
|
135 |
+
def format_citation_html(url, fragment_text, author, year, scc_hash):
|
136 |
+
encoded_fragment = urllib.parse.quote(fragment_text)
|
137 |
+
full_url = f"{url}#:~:text={encoded_fragment}"
|
138 |
+
return f'<a href="{full_url}" data-hash="{scc_hash}">({author}, {year})</a>'
|
139 |
+
|
140 |
+
def format_citation_plaintext(author, year, scc_hash):
|
141 |
+
return f'({author}, {year}) [hash: {scc_hash[:12]}...]'
|
142 |
+
|
143 |
+
def check_for_fragment(url):
|
144 |
+
return '#:~:text=' in url
|
145 |
+
|
146 |
+
def copy_to_clipboard_js(text, button_id):
|
147 |
+
"""Generate JavaScript for copying text to clipboard"""
|
148 |
+
return f"""
|
149 |
+
<script>
|
150 |
+
function copyToClipboard_{button_id}() {{
|
151 |
+
navigator.clipboard.writeText(`{text}`).then(function() {{
|
152 |
+
document.getElementById('copy_status_{button_id}').innerHTML = 'β
Copied!';
|
153 |
+
setTimeout(function() {{
|
154 |
+
document.getElementById('copy_status_{button_id}').innerHTML = '';
|
155 |
+
}}, 2000);
|
156 |
+
}}, function(err) {{
|
157 |
+
document.getElementById('copy_status_{button_id}').innerHTML = 'β Copy failed';
|
158 |
+
console.error('Could not copy text: ', err);
|
159 |
+
}});
|
160 |
+
}}
|
161 |
+
</script>
|
162 |
+
<button onclick="copyToClipboard_{button_id}()" class="copy-button">π Copy to Clipboard</button>
|
163 |
+
<span id="copy_status_{button_id}" style="margin-left: 10px; color: green; font-weight: bold;"></span>
|
164 |
+
"""
|
165 |
+
|
166 |
+
# --- Streamlit App ---
|
167 |
+
st.set_page_config(layout="wide", page_title="Smart Context Citation Tool", page_icon="π")
|
168 |
+
|
169 |
+
# Load custom CSS
|
170 |
+
load_css()
|
171 |
+
|
172 |
+
# Main header
|
173 |
+
st.markdown("""
|
174 |
+
<div class="main-header">
|
175 |
+
<h1>π Smart Context Citation (SCC) Tool</h1>
|
176 |
+
<p>Next-generation digital referencing system for the age of Generative AI</p>
|
177 |
+
</div>
|
178 |
+
""", unsafe_allow_html=True)
|
179 |
+
|
180 |
+
# Creator information
|
181 |
+
st.markdown("""
|
182 |
+
<div class="creator-info">
|
183 |
+
<strong>Developed by:</strong> Dr Majed Abuseif<br>
|
184 |
+
School of Architecture and Built Environment<br>
|
185 |
+
Deakin University<br>
|
186 |
+
Β© 2025
|
187 |
+
</div>
|
188 |
+
""", unsafe_allow_html=True)
|
189 |
+
|
190 |
+
st.sidebar.header("π About SCC")
|
191 |
+
st.sidebar.markdown("""
|
192 |
+
<div class="info-card">
|
193 |
+
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.
|
194 |
+
|
195 |
+
<strong>π― Purpose:</strong> Transparency, integrity, and digital fluency in citations.
|
196 |
+
|
197 |
+
<strong>ποΈ Structure:</strong>
|
198 |
+
β’ Inline general author name and date style citation
|
199 |
+
β’ Hyperlinked URL with text fragment (#:~:text=)
|
200 |
+
β’ SHA-256 hash for verification
|
201 |
+
|
202 |
+
<strong>β¨ Benefits:</strong> Enhances fairness, integrates with source contexts, promotes digital fluency, prevents fabrication, and eliminates traditional reference lists.
|
203 |
+
|
204 |
+
<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.
|
205 |
+
</div>
|
206 |
+
""", unsafe_allow_html=True)
|
207 |
+
|
208 |
+
st.sidebar.header("π‘ Example Citation")
|
209 |
+
st.sidebar.markdown("""
|
210 |
+
<div class="info-card">
|
211 |
+
<strong>π Input:</strong><br>
|
212 |
+
β’ Author: <code>Abuseif et al.</code><br>
|
213 |
+
β’ Year: <code>2025</code><br>
|
214 |
+
β’ URL: <code>https://www.sciencedirect.com/science/article/pii/S2772411523000046</code><br>
|
215 |
+
β’ Text: <code>A proposed design framework for green roof settings in general and trees on buildings</code>
|
216 |
+
|
217 |
+
<strong>π€ Output (HTML - example hash):</strong><br>
|
218 |
+
<div class="hash-display">
|
219 |
+
<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>
|
220 |
+
</div>
|
221 |
+
</div>
|
222 |
+
""", unsafe_allow_html=True)
|
223 |
+
|
224 |
+
tabs = st.tabs(["π§ Citation Generator", "β
Verify Citation"])
|
225 |
+
|
226 |
+
with tabs[0]:
|
227 |
+
st.markdown('<div class="tab-content">', unsafe_allow_html=True)
|
228 |
+
st.header("π§ Generate New Citation")
|
229 |
+
|
230 |
+
with st.container():
|
231 |
+
st.markdown('<div class="form-section">', unsafe_allow_html=True)
|
232 |
+
|
233 |
+
col1, col2 = st.columns(2)
|
234 |
+
|
235 |
+
with col1:
|
236 |
+
username = st.text_input("π€ Username", help="Your username for tracking purposes", placeholder="e.g., john_doe")
|
237 |
+
task_name = st.text_input("π Task Name", help="The name of the task or project", placeholder="e.g., Literature Review Assignment")
|
238 |
+
author_name = st.text_input("βοΈ Author Name", help="The author(s) of the source", placeholder="e.g., Smith or Smith et al.")
|
239 |
+
|
240 |
+
with col2:
|
241 |
+
publication_year = st.text_input("π
Publication Year", help="The year of publication", placeholder="e.g., 2023")
|
242 |
+
source_url = st.text_input("π Source URL", help="The full URL of the source", placeholder="https://example.com/article")
|
243 |
+
|
244 |
+
cited_text = st.text_area("π Quoted or Paraphrased Text",
|
245 |
+
help="The exact text quoted or paraphrased from the source",
|
246 |
+
placeholder="e.g., Thermal comfort thresholds are exceeded frequently in Australian schools",
|
247 |
+
height=100)
|
248 |
+
|
249 |
+
# Get current date and time in Melbourne timezone
|
250 |
+
melbourne_tz = pytz.timezone(MELBOURNE_TIMEZONE)
|
251 |
+
current_datetime_melbourne = datetime.now(melbourne_tz)
|
252 |
+
current_date = current_datetime_melbourne.strftime("%Y-%m-%d")
|
253 |
+
current_time = current_datetime_melbourne.strftime("%H:%M:%S")
|
254 |
+
|
255 |
+
st.markdown(f"""
|
256 |
+
<div class="datetime-display">
|
257 |
+
<strong>π Current Date & Time (Melbourne, Australia):</strong><br>
|
258 |
+
π
Date: {current_date}<br>
|
259 |
+
β° Time: {current_time}
|
260 |
+
</div>
|
261 |
+
""", unsafe_allow_html=True)
|
262 |
+
|
263 |
+
generate_button = st.button("π Generate Citation", type="primary", use_container_width=True)
|
264 |
+
|
265 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
266 |
+
|
267 |
+
if generate_button:
|
268 |
+
if not all([username, task_name, author_name, publication_year, source_url, cited_text]):
|
269 |
+
st.error("β οΈ Please fill in all fields before generating a citation.")
|
270 |
+
elif check_for_fragment(source_url):
|
271 |
+
st.markdown("""
|
272 |
+
<div class="warning-box">
|
273 |
+
<strong>β οΈ Warning:</strong> It seems like your URL already contains a text fragment (<code>#:~:text=</code>).
|
274 |
+
This suggests you may have used AI assistance in generating this link. Please go back to the original source,
|
275 |
+
read the context carefully, and copy the source link again without any existing fragment.
|
276 |
+
</div>
|
277 |
+
""", unsafe_allow_html=True)
|
278 |
+
else:
|
279 |
+
scc_hash = generate_citation_hash(author_name, publication_year, source_url, cited_text, cited_text, username, task_name, current_date, current_time)
|
280 |
+
|
281 |
+
st.markdown("## π Generated Citations")
|
282 |
+
|
283 |
+
# HTML Citation - Start of text
|
284 |
+
st.markdown("### π HTML Citation (Start of Text)")
|
285 |
+
html_citation_start = f'"{cited_text}" ' + format_citation_html(source_url, cited_text, author_name, publication_year, scc_hash)
|
286 |
+
|
287 |
+
st.markdown('<div class="citation-output">', unsafe_allow_html=True)
|
288 |
+
st.code(html_citation_start, language='html')
|
289 |
+
st.markdown(copy_to_clipboard_js(html_citation_start.replace('`', '\\`'), "html_start"), unsafe_allow_html=True)
|
290 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
291 |
+
|
292 |
+
# HTML Citation - End of text
|
293 |
+
st.markdown("### π HTML Citation (End of Text)")
|
294 |
+
html_citation_end = format_citation_html(source_url, cited_text, author_name, publication_year, scc_hash) + f' "{cited_text}"'
|
295 |
+
|
296 |
+
st.markdown('<div class="citation-output">', unsafe_allow_html=True)
|
297 |
+
st.code(html_citation_end, language='html')
|
298 |
+
st.markdown(copy_to_clipboard_js(html_citation_end.replace('`', '\\`'), "html_end"), unsafe_allow_html=True)
|
299 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
300 |
+
|
301 |
+
# Plaintext Citation
|
302 |
+
st.markdown("### π Plaintext Citation")
|
303 |
+
plaintext_citation = format_citation_plaintext(author_name, publication_year, scc_hash)
|
304 |
+
|
305 |
+
st.markdown('<div class="citation-output">', unsafe_allow_html=True)
|
306 |
+
st.code(plaintext_citation)
|
307 |
+
st.markdown(copy_to_clipboard_js(plaintext_citation, "plaintext"), unsafe_allow_html=True)
|
308 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
309 |
+
|
310 |
+
# Citation Hash Details
|
311 |
+
st.markdown("### π Citation Hash Details (for Verification)")
|
312 |
+
hash_details = {
|
313 |
+
"author": author_name,
|
314 |
+
"year": publication_year,
|
315 |
+
"url": source_url,
|
316 |
+
"fragment_text": cited_text,
|
317 |
+
"cited_text": cited_text,
|
318 |
+
"username": username,
|
319 |
+
"task_name": task_name,
|
320 |
+
"date": current_date,
|
321 |
+
"time": current_time,
|
322 |
+
"hash": scc_hash
|
323 |
+
}
|
324 |
+
|
325 |
+
st.markdown('<div class="hash-display">', unsafe_allow_html=True)
|
326 |
+
st.json(hash_details)
|
327 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
328 |
+
|
329 |
+
# Instructions section
|
330 |
+
st.markdown("## π Instructions for Copying to Word")
|
331 |
+
st.markdown("""
|
332 |
+
<div class="info-card">
|
333 |
+
<strong>To use the generated HTML citation in Microsoft Word:</strong><br><br>
|
334 |
+
1. π Copy the desired HTML citation (Start or End of Text) using the 'Copy to Clipboard' button<br>
|
335 |
+
2. π In Word, go to the 'Insert' tab<br>
|
336 |
+
3. π Click on 'Object' β 'Text from File...'<br>
|
337 |
+
4. π Select 'HTML Document' from the file type dropdown<br>
|
338 |
+
5. πΎ Paste the copied HTML into a new text file (e.g., using Notepad) and save it with a <code>.html</code> extension<br>
|
339 |
+
6. β
Select this <code>.html</code> file in the 'Text from File...' dialog<br><br>
|
340 |
+
|
341 |
+
<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.
|
342 |
+
</div>
|
343 |
+
""", unsafe_allow_html=True)
|
344 |
+
|
345 |
+
st.markdown("## π Guidance on Verifying Citations")
|
346 |
+
st.markdown("""
|
347 |
+
<div class="info-card">
|
348 |
+
To verify a citation, you can recompute the hash using the original input data and compare it to the embedded hash.
|
349 |
+
The <strong>'Verify Citation'</strong> tab allows you to do this easily.
|
350 |
+
</div>
|
351 |
+
""", unsafe_allow_html=True)
|
352 |
+
|
353 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
354 |
+
|
355 |
+
with tabs[1]:
|
356 |
+
st.markdown('<div class="tab-content">', unsafe_allow_html=True)
|
357 |
+
st.header("β
Verify Citation")
|
358 |
+
st.markdown("""
|
359 |
+
<div class="info-card">
|
360 |
+
Enter the citation details below to recompute and verify the hash. This ensures the citation hasn't been tampered with or fabricated.
|
361 |
+
</div>
|
362 |
+
""", unsafe_allow_html=True)
|
363 |
+
|
364 |
+
# Initialize session state for storing verified hashes
|
365 |
+
if 'verified_hashes' not in st.session_state:
|
366 |
+
st.session_state.verified_hashes = []
|
367 |
+
|
368 |
+
with st.container():
|
369 |
+
st.markdown('<div class="form-section">', unsafe_allow_html=True)
|
370 |
+
|
371 |
+
col1, col2 = st.columns(2)
|
372 |
+
|
373 |
+
with col1:
|
374 |
+
verify_username = st.text_input("π€ Username (for verification)", placeholder="e.g., john_doe")
|
375 |
+
verify_task_name = st.text_input("π Task Name (for verification)", placeholder="e.g., Literature Review Assignment")
|
376 |
+
verify_author_name = st.text_input("βοΈ Author Name (for verification)", placeholder="e.g., Smith or Smith et al.")
|
377 |
+
verify_publication_year = st.text_input("π
Publication Year (for verification)", placeholder="e.g., 2023")
|
378 |
+
|
379 |
+
with col2:
|
380 |
+
verify_source_url = st.text_input("π Source URL (for verification)", placeholder="https://example.com/article")
|
381 |
+
verify_date = st.text_input("π
Date (YYYY-MM-DD) (for verification)", placeholder="e.g., 2025-01-08")
|
382 |
+
verify_time = st.text_input("β° Time (HH:MM:SS) (for verification)", placeholder="e.g., 14:30:25")
|
383 |
+
expected_hash = st.text_input("π Expected Hash (from the citation)", placeholder="Enter the full hash from the citation")
|
384 |
+
|
385 |
+
verify_cited_text = st.text_area("π Quoted or Paraphrased Text (for verification)",
|
386 |
+
placeholder="e.g., Thermal comfort thresholds are exceeded frequently in Australian schools",
|
387 |
+
height=100)
|
388 |
+
|
389 |
+
verify_button = st.button("π Verify Hash", type="primary", use_container_width=True)
|
390 |
+
|
391 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
392 |
+
|
393 |
+
if verify_button:
|
394 |
+
if not all([verify_username, verify_task_name, verify_author_name, verify_publication_year,
|
395 |
+
verify_source_url, verify_cited_text, verify_date, verify_time, expected_hash]):
|
396 |
+
st.error("β οΈ Please fill in all fields before verifying the hash.")
|
397 |
+
else:
|
398 |
+
recomputed_hash = generate_citation_hash(
|
399 |
+
verify_author_name, verify_publication_year, verify_source_url,
|
400 |
+
verify_cited_text, verify_cited_text, verify_username, verify_task_name,
|
401 |
+
verify_date, verify_time
|
402 |
+
)
|
403 |
+
|
404 |
+
if recomputed_hash == expected_hash:
|
405 |
+
st.markdown("""
|
406 |
+
<div class="success-box">
|
407 |
+
<strong>β
Hash verified successfully!</strong> The citation is authentic and hasn't been tampered with.
|
408 |
+
</div>
|
409 |
+
""", unsafe_allow_html=True)
|
410 |
+
|
411 |
+
st.session_state.verified_hashes.append({
|
412 |
+
"Author": verify_author_name,
|
413 |
+
"Year": verify_publication_year,
|
414 |
+
"URL": verify_source_url,
|
415 |
+
"Fragment text": verify_cited_text,
|
416 |
+
"Cited text": verify_cited_text,
|
417 |
+
"Username": verify_username,
|
418 |
+
"Task name": verify_task_name,
|
419 |
+
"Date": verify_date,
|
420 |
+
"Time": verify_time,
|
421 |
+
"Original Hash": expected_hash,
|
422 |
+
"Recomputed Hash": recomputed_hash,
|
423 |
+
"Status": "β
Verified"
|
424 |
+
})
|
425 |
+
else:
|
426 |
+
st.markdown("""
|
427 |
+
<div class="warning-box">
|
428 |
+
<strong>β Hash verification failed!</strong> The citation may have been altered or is not authentic.
|
429 |
+
</div>
|
430 |
+
""", unsafe_allow_html=True)
|
431 |
+
|
432 |
+
st.session_state.verified_hashes.append({
|
433 |
+
"Author": verify_author_name,
|
434 |
+
"Year": verify_publication_year,
|
435 |
+
"URL": verify_source_url,
|
436 |
+
"Fragment text": verify_cited_text,
|
437 |
+
"Cited text": verify_cited_text,
|
438 |
+
"Username": verify_username,
|
439 |
+
"Task name": verify_task_name,
|
440 |
+
"Date": verify_date,
|
441 |
+
"Time": verify_time,
|
442 |
+
"Original Hash": expected_hash,
|
443 |
+
"Recomputed Hash": recomputed_hash,
|
444 |
+
"Status": "β Failed"
|
445 |
+
})
|
446 |
+
|
447 |
+
if st.session_state.verified_hashes:
|
448 |
+
st.markdown("## π Verification History")
|
449 |
+
df = pd.DataFrame(st.session_state.verified_hashes)
|
450 |
+
|
451 |
+
st.markdown('<div class="verification-table">', unsafe_allow_html=True)
|
452 |
+
st.dataframe(df, use_container_width=True)
|
453 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
454 |
+
|
455 |
+
# Download as CSV
|
456 |
+
@st.cache_data
|
457 |
+
def convert_df_to_csv(df):
|
458 |
+
return df.to_csv(index=False).encode('utf-8')
|
459 |
+
|
460 |
+
csv = convert_df_to_csv(df)
|
461 |
+
|
462 |
+
st.download_button(
|
463 |
+
label="π₯ Download Verification History as CSV",
|
464 |
+
data=csv,
|
465 |
+
file_name="scc_verification_history.csv",
|
466 |
+
mime="text/csv",
|
467 |
+
use_container_width=True
|
468 |
+
)
|
469 |
+
|
470 |
+
# Clear history button
|
471 |
+
if st.button("ποΈ Clear Verification History", type="secondary"):
|
472 |
+
st.session_state.verified_hashes = []
|
473 |
+
st.experimental_rerun()
|
474 |
+
|
475 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
476 |
+
|
477 |
+
|
478 |
+
|
479 |
+
|
480 |
+
|
requirements.txt
CHANGED
@@ -1,3 +1,4 @@
|
|
1 |
-
|
|
|
2 |
pandas
|
3 |
-
|
|
|
1 |
+
streamlit
|
2 |
+
pytz
|
3 |
pandas
|
4 |
+
|