Update document handling
Browse files- app.py +110 -11
- static/script.js +45 -51
- static/style.css +5 -0
app.py
CHANGED
@@ -128,6 +128,114 @@ def get_latest_version(spec: str) -> str:
|
|
128 |
else:
|
129 |
raise HTTPException(req.status_code, "An error has occured while getting latest version")
|
130 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
131 |
class SpecRequest(BaseModel):
|
132 |
specification: str
|
133 |
version: Optional[str] = None
|
@@ -137,7 +245,7 @@ def main_page():
|
|
137 |
return FileResponse(os.path.join("templates", "index.html"))
|
138 |
|
139 |
@app.post("/from-search")
|
140 |
-
def get_file_from_spec_id_version(req: SpecRequest) -> Dict
|
141 |
spec = req.specification
|
142 |
version = req.version
|
143 |
if not version:
|
@@ -176,13 +284,4 @@ def get_file_from_spec_id_version(req: SpecRequest) -> Dict[str, str]:
|
|
176 |
except ValueError as e:
|
177 |
real_toc_indexes[chapter] = -float("inf")
|
178 |
|
179 |
-
|
180 |
-
toc = list(real_toc_indexes.keys())
|
181 |
-
index_toc = list(real_toc_indexes.values())
|
182 |
-
curr_index = 0
|
183 |
-
for x in range(1, len(toc)):
|
184 |
-
document[toc[curr_index].replace("\t", " ")] = re.sub(r"[\ \t]+", " ", "\n".join(text[index_toc[curr_index]+1:index_toc[x]]))
|
185 |
-
curr_index = x
|
186 |
-
|
187 |
-
document[toc[curr_index].replace("\t"," ")] = re.sub(r"\s+", " ", " ".join(text[index_toc[curr_index]+1:]))
|
188 |
-
return document
|
|
|
128 |
else:
|
129 |
raise HTTPException(req.status_code, "An error has occured while getting latest version")
|
130 |
|
131 |
+
def create_nested_structure(chapters, text, real_toc_indexes):
|
132 |
+
"""Crée une structure hiérarchique où les sous-sections sont imbriquées dans leurs sections parentes."""
|
133 |
+
result = {}
|
134 |
+
|
135 |
+
# Trier les chapitres par numéro de section
|
136 |
+
sorted_chapters = sorted(chapters, key=lambda x: [int(p) if p.isdigit() else p for p in x.split()[0].split('.')])
|
137 |
+
|
138 |
+
# Préparer les contenus des chapitres
|
139 |
+
chapter_contents = {}
|
140 |
+
for i, chapter in enumerate(sorted_chapters):
|
141 |
+
current_index = real_toc_indexes[chapter]
|
142 |
+
|
143 |
+
# Déterminer l'index de fin
|
144 |
+
end_index = len(text)
|
145 |
+
if i < len(sorted_chapters) - 1:
|
146 |
+
next_chapter = sorted_chapters[i + 1]
|
147 |
+
end_index = real_toc_indexes[next_chapter]
|
148 |
+
|
149 |
+
# Extraire et nettoyer le contenu
|
150 |
+
content = text[current_index + 1:end_index]
|
151 |
+
cleaned_content = "\n".join(content).strip()
|
152 |
+
chapter_contents[chapter] = cleaned_content
|
153 |
+
|
154 |
+
# Fonction récursive pour construire la structure hiérarchique
|
155 |
+
def insert_section(root, section_path, title, content):
|
156 |
+
"""Insère une section dans l'arborescence hiérarchique."""
|
157 |
+
parts = section_path.split('.')
|
158 |
+
|
159 |
+
# Ignorer les sections sans titre réel (seulement si le titre est vide ou juste des espaces)
|
160 |
+
if not title.strip():
|
161 |
+
# Si c'est une section sans titre mais avec du contenu, on peut le fusionner avec sa première sous-section
|
162 |
+
# ou simplement l'ignorer selon votre besoin
|
163 |
+
return None
|
164 |
+
|
165 |
+
# Cas de base: section de premier niveau
|
166 |
+
if len(parts) == 1:
|
167 |
+
key = section_path + " " + title
|
168 |
+
if key not in root:
|
169 |
+
root[key] = {"content": content, "subsections": {}}
|
170 |
+
else:
|
171 |
+
root[key]["content"] = content
|
172 |
+
return root[key]
|
173 |
+
|
174 |
+
# Trouver ou créer le parent
|
175 |
+
parent_path = '.'.join(parts[:-1])
|
176 |
+
for key in root.keys():
|
177 |
+
if key.startswith(parent_path + " "):
|
178 |
+
# Parent trouvé, insérer dans ses sous-sections
|
179 |
+
section_key = section_path + " " + title
|
180 |
+
if section_key not in root[key]["subsections"]:
|
181 |
+
root[key]["subsections"][section_key] = {"content": content, "subsections": {}}
|
182 |
+
else:
|
183 |
+
root[key]["subsections"][section_key]["content"] = content
|
184 |
+
return root[key]["subsections"][section_key]
|
185 |
+
|
186 |
+
# Parent non trouvé, il faut le créer d'abord
|
187 |
+
# Rechercher le titre du parent
|
188 |
+
parent_title = ""
|
189 |
+
for chapter in sorted_chapters:
|
190 |
+
if chapter.split()[0] == parent_path:
|
191 |
+
parts = chapter.split(maxsplit=1)
|
192 |
+
parent_title = parts[1] if len(parts) > 1 else ""
|
193 |
+
break
|
194 |
+
|
195 |
+
# Si le parent n'a pas de titre, on cherche un parent plus haut
|
196 |
+
if not parent_title.strip():
|
197 |
+
# On peut soit ignorer cette branche, soit essayer de trouver un parent valide plus haut
|
198 |
+
grand_parent_parts = parent_path.split('.')
|
199 |
+
if len(grand_parent_parts) > 1:
|
200 |
+
grand_parent_path = '.'.join(grand_parent_parts[:-1])
|
201 |
+
for key in root.keys():
|
202 |
+
if key.startswith(grand_parent_path + " "):
|
203 |
+
# On a trouvé un grand-parent valide, on insère directement dedans
|
204 |
+
section_key = section_path + " " + title
|
205 |
+
if section_key not in root[key]["subsections"]:
|
206 |
+
root[key]["subsections"][section_key] = {"content": content, "subsections": {}}
|
207 |
+
return root[key]["subsections"][section_key]
|
208 |
+
# Si on n'a pas trouvé de grand-parent valide, on insère à la racine
|
209 |
+
section_key = section_path + " " + title
|
210 |
+
root[section_key] = {"content": content, "subsections": {}}
|
211 |
+
return root[section_key]
|
212 |
+
|
213 |
+
# Créer le parent récursivement
|
214 |
+
parent_section = insert_section(root, parent_path, parent_title, "")
|
215 |
+
|
216 |
+
# Si le parent n'a pas pu être créé (car sans titre), on insère à la racine
|
217 |
+
if parent_section is None:
|
218 |
+
section_key = section_path + " " + title
|
219 |
+
root[section_key] = {"content": content, "subsections": {}}
|
220 |
+
return root[section_key]
|
221 |
+
|
222 |
+
# Maintenant insérer cette section dans le parent nouvellement créé
|
223 |
+
section_key = section_path + " " + title
|
224 |
+
parent_section["subsections"][section_key] = {"content": content, "subsections": {}}
|
225 |
+
return parent_section["subsections"][section_key]
|
226 |
+
|
227 |
+
# Traiter chaque chapitre
|
228 |
+
for chapter in sorted_chapters:
|
229 |
+
parts = chapter.split(maxsplit=1)
|
230 |
+
section_num = parts[0]
|
231 |
+
section_title = parts[1] if len(parts) > 1 else ""
|
232 |
+
|
233 |
+
# Ne traiter que les sections avec un titre
|
234 |
+
if section_title.strip():
|
235 |
+
insert_section(result, section_num, section_title, chapter_contents[chapter])
|
236 |
+
|
237 |
+
return result
|
238 |
+
|
239 |
class SpecRequest(BaseModel):
|
240 |
specification: str
|
241 |
version: Optional[str] = None
|
|
|
245 |
return FileResponse(os.path.join("templates", "index.html"))
|
246 |
|
247 |
@app.post("/from-search")
|
248 |
+
def get_file_from_spec_id_version(req: SpecRequest) -> Dict:
|
249 |
spec = req.specification
|
250 |
version = req.version
|
251 |
if not version:
|
|
|
284 |
except ValueError as e:
|
285 |
real_toc_indexes[chapter] = -float("inf")
|
286 |
|
287 |
+
return create_nested_structure(chapters, text, real_toc_indexes)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static/script.js
CHANGED
@@ -25,12 +25,6 @@ function handleSpecSearch(){
|
|
25 |
let body = {"specification": document.getElementById("specInput").value};
|
26 |
if(versCheck && document.getElementById("versionInput").value.length > 0){
|
27 |
body["version"] = document.getElementById("versionInput").value;
|
28 |
-
} else {
|
29 |
-
fetch("https://organizedprogrammers-3gppdocfinder.hf.space/find",
|
30 |
-
{method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({"doc_id": body.specification})}
|
31 |
-
).then(data => data.json())
|
32 |
-
.then(resp => body['version'] = resp.version)
|
33 |
-
.catch(error => console.error(error))
|
34 |
}
|
35 |
|
36 |
fetch("/from-search", {
|
@@ -91,57 +85,57 @@ function handleFileSelect(event) {
|
|
91 |
reader.readAsText(file);
|
92 |
}
|
93 |
|
94 |
-
function renderDocument(tab,
|
95 |
const container = document.querySelector(`#${tab} #document-container`);
|
96 |
container.innerHTML = '';
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
container.appendChild(section);
|
115 |
-
} else if (key.match(/^\d+\.\d+$/)) {
|
116 |
-
// Sous-section (ex: "3.1 Definitions")
|
117 |
-
const subsection = document.createElement('div');
|
118 |
-
subsection.className = 'subsection';
|
119 |
-
|
120 |
-
const title = document.createElement('h3');
|
121 |
-
title.textContent = key;
|
122 |
-
subsection.appendChild(title);
|
123 |
-
|
124 |
-
const content = document.createElement('div');
|
125 |
-
content.innerHTML = formatText(value);
|
126 |
-
subsection.appendChild(content);
|
127 |
-
|
128 |
-
container.appendChild(subsection);
|
129 |
-
} else {
|
130 |
-
// Autres éléments
|
131 |
-
const div = document.createElement('div');
|
132 |
-
const title = document.createElement('h3');
|
133 |
-
title.textContent = key;
|
134 |
-
div.appendChild(title);
|
135 |
-
|
136 |
const content = document.createElement('div');
|
137 |
-
content.
|
138 |
-
|
139 |
-
|
140 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
}
|
142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
143 |
}
|
144 |
|
|
|
|
|
145 |
function formatText(text) {
|
146 |
if (!text) return '';
|
147 |
|
|
|
25 |
let body = {"specification": document.getElementById("specInput").value};
|
26 |
if(versCheck && document.getElementById("versionInput").value.length > 0){
|
27 |
body["version"] = document.getElementById("versionInput").value;
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
}
|
29 |
|
30 |
fetch("/from-search", {
|
|
|
85 |
reader.readAsText(file);
|
86 |
}
|
87 |
|
88 |
+
function renderDocument(tab, data) {
|
89 |
const container = document.querySelector(`#${tab} #document-container`);
|
90 |
container.innerHTML = '';
|
91 |
+
|
92 |
+
function renderSection(sectionKey, sectionData, level) {
|
93 |
+
const sectionDiv = document.createElement('div');
|
94 |
+
sectionDiv.className = `section level-${level}`;
|
95 |
+
|
96 |
+
// Extraire le numéro et le titre de la clé
|
97 |
+
const parts = sectionKey.split(/\s(.+)/); // Divise à partir du premier espace
|
98 |
+
const sectionNumber = parts[0];
|
99 |
+
const sectionTitle = parts[1] || "";
|
100 |
+
|
101 |
+
// Créer l'en-tête de section
|
102 |
+
const header = document.createElement(`h${Math.min(level + 1, 6)}`);
|
103 |
+
header.textContent = sectionKey; // Utiliser la clé complète comme titre
|
104 |
+
sectionDiv.appendChild(header);
|
105 |
+
|
106 |
+
// Ajouter le contenu
|
107 |
+
if (sectionData.content) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
const content = document.createElement('div');
|
109 |
+
content.className = 'content';
|
110 |
+
content.textContent = sectionData.content;
|
111 |
+
sectionDiv.appendChild(content);
|
112 |
+
}
|
113 |
+
|
114 |
+
// Ajouter les sous-sections récursivement
|
115 |
+
if (sectionData.subsections && Object.keys(sectionData.subsections).length > 0) {
|
116 |
+
const subsectionsDiv = document.createElement('div');
|
117 |
+
subsectionsDiv.className = 'subsections';
|
118 |
+
|
119 |
+
for (const [subKey, subData] of Object.entries(sectionData.subsections)) {
|
120 |
+
const subSection = renderSection(subKey, subData, level + 1);
|
121 |
+
subsectionsDiv.appendChild(subSection);
|
122 |
+
}
|
123 |
+
|
124 |
+
sectionDiv.appendChild(subsectionsDiv);
|
125 |
}
|
126 |
+
|
127 |
+
return sectionDiv;
|
128 |
+
}
|
129 |
+
|
130 |
+
// Parcourir les sections de premier niveau
|
131 |
+
for (const [sectionKey, sectionData] of Object.entries(data)) {
|
132 |
+
const sectionElement = renderSection(sectionKey, sectionData, 1);
|
133 |
+
container.appendChild(sectionElement);
|
134 |
+
}
|
135 |
}
|
136 |
|
137 |
+
|
138 |
+
|
139 |
function formatText(text) {
|
140 |
if (!text) return '';
|
141 |
|
static/style.css
CHANGED
@@ -7,6 +7,11 @@ body {
|
|
7 |
color: #333;
|
8 |
}
|
9 |
|
|
|
|
|
|
|
|
|
|
|
10 |
.header {
|
11 |
margin-bottom: 30px;
|
12 |
padding-bottom: 15px;
|
|
|
7 |
color: #333;
|
8 |
}
|
9 |
|
10 |
+
div[class^='section level']{
|
11 |
+
padding: 10px;
|
12 |
+
border: 1px solid #bbb;
|
13 |
+
}
|
14 |
+
|
15 |
.header {
|
16 |
margin-bottom: 30px;
|
17 |
padding-bottom: 15px;
|