v2.0.8: конвертация .doc → .docx через convert-service перед upload
Deploy contracts-flask / validate (push) Successful in 0s
Deploy contracts-flask / validate (push) Successful in 0s
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
"""Конфигурация приложения — все настройки в одном месте."""
|
"""Конфигурация приложения — все настройки в одном месте."""
|
||||||
import os
|
import os
|
||||||
|
|
||||||
VERSION = "2.0.7"
|
VERSION = "2.0.8"
|
||||||
|
|
||||||
LLM_URL = os.getenv("LLM_API_URL", "https://api.aillm.ru/v1/chat/completions")
|
LLM_URL = os.getenv("LLM_API_URL", "https://api.aillm.ru/v1/chat/completions")
|
||||||
LLM_KEY = os.getenv("LLM_API_KEY", "")
|
LLM_KEY = os.getenv("LLM_API_KEY", "")
|
||||||
|
|||||||
+57
-2
@@ -12,6 +12,8 @@
|
|||||||
* - statusToHTML() — рендер статуса (↑ N%, ✓)
|
* - statusToHTML() — рендер статуса (↑ N%, ✓)
|
||||||
* - renderFiles() — рендер всей таблицы
|
* - renderFiles() — рендер всей таблицы
|
||||||
|
|
||||||
|
var CONVERT_URL = '/convert-doc';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* statusToHTML(status) — Чистая функция: структура → HTML (Фаза 1).
|
* statusToHTML(status) — Чистая функция: структура → HTML (Фаза 1).
|
||||||
*
|
*
|
||||||
@@ -26,6 +28,7 @@ function statusToHTML(st) {
|
|||||||
if (!st || !st.kind) return '';
|
if (!st || !st.kind) return '';
|
||||||
switch (st.kind) {
|
switch (st.kind) {
|
||||||
case 'connecting': return '⏳ соединение... ' + (st.elapsed || 0) + 'с';
|
case 'connecting': return '⏳ соединение... ' + (st.elapsed || 0) + 'с';
|
||||||
|
case 'converting': return '⏳ конвертация... ' + (st.elapsed || 0) + 'с';
|
||||||
case 'uploading': return '⏳ отправка... ' + (st.elapsed || 0) + 'с';
|
case 'uploading': return '⏳ отправка... ' + (st.elapsed || 0) + 'с';
|
||||||
case 'uploaded': return '<span class="status-ok">✓</span>';
|
case 'uploaded': return '<span class="status-ok">✓</span>';
|
||||||
case 'unzipping': return '⏳ распаковка...';
|
case 'unzipping': return '⏳ распаковка...';
|
||||||
@@ -203,6 +206,40 @@ window.toggleClassifyDetail = async function(i) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convertDoc(file, onProgress) — .doc → .docx через внешний libreoffice-сервис.
|
||||||
|
* С честным счётчиком времени.
|
||||||
|
*/
|
||||||
|
function convertDoc(file, onProgress) {
|
||||||
|
var startTime = Date.now();
|
||||||
|
if (onProgress) onProgress({ kind: 'converting', elapsed: 0 });
|
||||||
|
var fd = new FormData();
|
||||||
|
fd.append('files', file, file.name);
|
||||||
|
|
||||||
|
var timer = setInterval(function() {
|
||||||
|
var elapsed = Math.floor((Date.now() - startTime) / 1000);
|
||||||
|
if (onProgress) onProgress({ kind: 'converting', elapsed: elapsed });
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return fetch(CONVERT_URL + '?_=' + Date.now(), { method: 'POST', body: fd })
|
||||||
|
.then(function(r) {
|
||||||
|
if (!r.ok) throw new Error('Конвертация: HTTP ' + r.status);
|
||||||
|
return r.blob();
|
||||||
|
})
|
||||||
|
.then(function(blob) {
|
||||||
|
clearInterval(timer);
|
||||||
|
if (blob.size === 0) throw new Error('Конвертация: пустой результат');
|
||||||
|
return new File([blob], file.name.replace(/\.doc$/i, '.docx'), {
|
||||||
|
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||||
|
lastModified: Date.now()
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(function(e) {
|
||||||
|
clearInterval(timer);
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ⛔ НЕ МЕНЯТЬ ⛔ uploadFile — fetch-загрузка с честным счётчиком времени.
|
* ⛔ НЕ МЕНЯТЬ ⛔ uploadFile — fetch-загрузка с честным счётчиком времени.
|
||||||
*
|
*
|
||||||
@@ -430,8 +467,17 @@ async function addRegularFile(file, zipSource) {
|
|||||||
render(state);
|
render(state);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// .doc → конвертация через libreoffice-сервис
|
||||||
|
var uploadTarget = file;
|
||||||
|
if (file.name.toLowerCase().endsWith('.doc')) {
|
||||||
|
uploadTarget = await convertDoc(file, function(st) {
|
||||||
|
state.files[rowIdx].status = st;
|
||||||
|
render(state);
|
||||||
|
});
|
||||||
|
state.files[rowIdx].name = uploadTarget.name;
|
||||||
|
}
|
||||||
// fetch-загрузка с честным счётчиком времени
|
// fetch-загрузка с честным счётчиком времени
|
||||||
var resp = await uploadFile(file, function(st) {
|
var resp = await uploadFile(uploadTarget, function(st) {
|
||||||
state.files[rowIdx].status = st;
|
state.files[rowIdx].status = st;
|
||||||
render(state);
|
render(state);
|
||||||
}, zipSource);
|
}, zipSource);
|
||||||
@@ -523,7 +569,16 @@ async function onFilesSelected(newFiles) {
|
|||||||
delete entry._pendingFile;
|
delete entry._pendingFile;
|
||||||
var f = pendingFile;
|
var f = pendingFile;
|
||||||
try {
|
try {
|
||||||
var resp = await uploadFile(f, function(st) {
|
// .doc → конвертация через libreoffice-сервис
|
||||||
|
var uploadTarget = f;
|
||||||
|
if (f.name.toLowerCase().endsWith('.doc')) {
|
||||||
|
uploadTarget = await convertDoc(f, function(st) {
|
||||||
|
entry.status = st;
|
||||||
|
render(state);
|
||||||
|
});
|
||||||
|
entry.name = uploadTarget.name;
|
||||||
|
}
|
||||||
|
var resp = await uploadFile(uploadTarget, function(st) {
|
||||||
entry.status = st;
|
entry.status = st;
|
||||||
render(state);
|
render(state);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -73,7 +73,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="topbar">
|
<div class="topbar">
|
||||||
<img src="/static/logo.svg" alt="Nubes">
|
<img src="/static/logo.svg" alt="Nubes">
|
||||||
<span class="title">Сверка договоров — LLM AI-driven Event Sourcing <span style="font-weight:400;color:var(--muted);font-size:12px;">v2.0.7</span></span>
|
<span class="title">Сверка договоров — LLM AI-driven Event Sourcing <span style="font-weight:400;color:var(--muted);font-size:12px;">v2.0.8</span></span>
|
||||||
<div id="pipelineStepper" style="display:flex;gap:8px;font-size:11px;align-items:center;color:var(--muted);">
|
<div id="pipelineStepper" style="display:flex;gap:8px;font-size:11px;align-items:center;color:var(--muted);">
|
||||||
<span id="stepUpload">○ Загрузка</span><span>→</span>
|
<span id="stepUpload">○ Загрузка</span><span>→</span>
|
||||||
<span id="stepClassify">○ Классификация</span><span>→</span>
|
<span id="stepClassify">○ Классификация</span><span>→</span>
|
||||||
@@ -216,11 +216,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/state.js?v=2.0.7"></script>
|
<script src="/static/state.js?v=2.0.8"></script>
|
||||||
<script src="/static/app_utils.js?v=2.0.7"></script>
|
<script src="/static/app_utils.js?v=2.0.8"></script>
|
||||||
<script src="/static/files.js?v=2.0.7"></script>
|
<script src="/static/files.js?v=2.0.8"></script>
|
||||||
<script src="/static/groups.js?v=2.0.7"></script>
|
<script src="/static/groups.js?v=2.0.8"></script>
|
||||||
<script src="/static/compare.js?v=2.0.7"></script>
|
<script src="/static/compare.js?v=2.0.8"></script>
|
||||||
<script src="/static/app.js?v=2.0.7"></script>
|
<script src="/static/app.js?v=2.0.8"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user