feat(maria): depozit local, in afara oglinzii Drive
Dictionarul de erori Oracle pus in `documents/` ar fi disparut in 10 minute: `rclone sync` sterge de acolo tot ce nu exista in Drive. Aceeasi problema o avea si butonul de adaugare document din dashboard — documentul traia pana la urmatorul tur de sincronizare, ceea ce README-ul mentiona ca pe o ciudatenie, nu ca pe un bug. Acum sunt doua directoare cu un singur spatiu de nume: `documents/` (oglinda Drive) si `documents-local/` (ce nu vine din Drive). La acelasi nume castiga Drive-ul — e sursa comuna a echipei, iar copia locala poate fi o versiune veche uitata acolo. Dashboard-ul scrie in cel local si marcheaza documentele „local". Recalibrat cu dictionarul indexat (169 chunk-uri): 19/19, cele patru intrebari Oracle noi ies la 0,73-0,75. Reindexarea a durat cat cele 29 de chunk-uri noi, nu cat toate 169 — refolosirea vectorilor isi face treaba (log: "29 embeddings noi, 140 refolosite"). Verificat pe canalul viu: o escaladare cu captura chiar pleaca pe WhatsApp, imagine + rezumat + referinta, si apare in dashboard. 73 pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
This commit is contained in:
@@ -61,6 +61,9 @@ MARIA_DOC_EXTENSIONS = (".txt", ".md", ".xml")
|
|||||||
_MARIA_FORMAT_RANK = {".xml": 0, ".md": 1, ".txt": 2}
|
_MARIA_FORMAT_RANK = {".xml": 0, ".md": 1, ".txt": 2}
|
||||||
MARIA_STATE_DIR = Path(os.environ.get("MARIA_BRIDGE_DIR") or (Path.home() / ".maria-bridge"))
|
MARIA_STATE_DIR = Path(os.environ.get("MARIA_BRIDGE_DIR") or (Path.home() / ".maria-bridge"))
|
||||||
MARIA_DOCS_DIR = MARIA_STATE_DIR / "documents"
|
MARIA_DOCS_DIR = MARIA_STATE_DIR / "documents"
|
||||||
|
# Documentele care nu vin din Drive (rclone sync sterge din MARIA_DOCS_DIR tot ce
|
||||||
|
# nu exista in Drive). Oglinda lui rag/config.py:DOCS_LOCAL_DIR.
|
||||||
|
MARIA_DOCS_LOCAL_DIR = MARIA_STATE_DIR / "documents-local"
|
||||||
MARIA_INDEX_FILE = MARIA_STATE_DIR / "rag_index.json"
|
MARIA_INDEX_FILE = MARIA_STATE_DIR / "rag_index.json"
|
||||||
MARIA_ENV_FILE = MARIA_STATE_DIR / "env"
|
MARIA_ENV_FILE = MARIA_STATE_DIR / "env"
|
||||||
MARIA_LOG_DIR = MARIA_STATE_DIR / "logs"
|
MARIA_LOG_DIR = MARIA_STATE_DIR / "logs"
|
||||||
@@ -144,10 +147,14 @@ def maria_documents() -> list[dict]:
|
|||||||
Nu importam modulul acela: si el, si `discord-bridge/config.py` s-ar numi `config`.
|
Nu importam modulul acela: si el, si `discord-bridge/config.py` s-ar numi `config`.
|
||||||
"""
|
"""
|
||||||
MARIA_DOCS_DIR.mkdir(parents=True, exist_ok=True)
|
MARIA_DOCS_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
files = [
|
vazute: dict[str, Path] = {}
|
||||||
f for f in sorted(MARIA_DOCS_DIR.glob("*"))
|
for d in (MARIA_DOCS_LOCAL_DIR, MARIA_DOCS_DIR): # Drive ultimul = castiga la acelasi nume
|
||||||
if f.is_file() and f.suffix in MARIA_DOC_EXTENSIONS
|
if not d.is_dir():
|
||||||
]
|
continue
|
||||||
|
for f in sorted(d.glob("*")):
|
||||||
|
if f.is_file() and f.suffix in MARIA_DOC_EXTENSIONS:
|
||||||
|
vazute[f.name] = f
|
||||||
|
files = [vazute[n] for n in sorted(vazute)]
|
||||||
winners: dict[str, str] = {}
|
winners: dict[str, str] = {}
|
||||||
for f in files:
|
for f in files:
|
||||||
best = winners.get(f.stem)
|
best = winners.get(f.stem)
|
||||||
@@ -162,6 +169,7 @@ def maria_documents() -> list[dict]:
|
|||||||
"name": f.name,
|
"name": f.name,
|
||||||
"size": st.st_size,
|
"size": st.st_size,
|
||||||
"mtime": st.st_mtime,
|
"mtime": st.st_mtime,
|
||||||
|
"local": f.parent == MARIA_DOCS_LOCAL_DIR,
|
||||||
"shadowed_by": None if winner == f.name else winner,
|
"shadowed_by": None if winner == f.name else winner,
|
||||||
})
|
})
|
||||||
return out
|
return out
|
||||||
@@ -206,18 +214,29 @@ def _maria_validate_name(name: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def maria_write_document(name: str, content: str) -> None:
|
def maria_write_document(name: str, content: str) -> None:
|
||||||
|
"""Scrie in documents-local/, nu in oglinda Drive.
|
||||||
|
|
||||||
|
Pana acum ajungea in `documents/` si disparea la urmatorul `rclone sync` —
|
||||||
|
documentul adaugat din dashboard traia pana la 10 minute.
|
||||||
|
"""
|
||||||
name = _maria_validate_name(name)
|
name = _maria_validate_name(name)
|
||||||
MARIA_DOCS_DIR.mkdir(parents=True, exist_ok=True)
|
MARIA_DOCS_LOCAL_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
(MARIA_DOCS_DIR / name).write_text(content, encoding="utf-8")
|
(MARIA_DOCS_LOCAL_DIR / name).write_text(content, encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
def maria_delete_document(name: str) -> bool:
|
def maria_delete_document(name: str) -> bool:
|
||||||
|
"""Sterge documentul din oricare din cele doua directoare.
|
||||||
|
|
||||||
|
Cel din Drive revine la urmatoarea sincronizare — sterge-l din Drive daca vrei
|
||||||
|
sa dispara definitiv.
|
||||||
|
"""
|
||||||
name = _maria_validate_name(name)
|
name = _maria_validate_name(name)
|
||||||
path = MARIA_DOCS_DIR / name
|
for d in (MARIA_DOCS_DIR, MARIA_DOCS_LOCAL_DIR):
|
||||||
if not path.exists():
|
path = d / name
|
||||||
return False
|
if path.exists():
|
||||||
path.unlink()
|
path.unlink()
|
||||||
return True
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _maria_run(script: str, timeout: float = 600.0, extra_args: list[str] | None = None) -> dict:
|
def _maria_run(script: str, timeout: float = 600.0, extra_args: list[str] | None = None) -> dict:
|
||||||
|
|||||||
@@ -466,10 +466,11 @@ async function mRefresh() {
|
|||||||
document.getElementById('mDocCount').textContent =
|
document.getElementById('mDocCount').textContent =
|
||||||
docs.length + ' în depozit' + (indexate !== docs.length ? ' · ' + indexate + ' indexate' : '');
|
docs.length + ' în depozit' + (indexate !== docs.length ? ' · ' + indexate + ' indexate' : '');
|
||||||
document.getElementById('mDocs').innerHTML = docs.length ? docs.map(function (d) {
|
document.getElementById('mDocs').innerHTML = docs.length ? docs.map(function (d) {
|
||||||
|
var local = d.local ? ' <span class="muted" title="nu vine din Drive; sincronizarea nu il sterge">· local</span>' : '';
|
||||||
var umbrit = d.shadowed_by
|
var umbrit = d.shadowed_by
|
||||||
? ' <span class="muted" title="acelaşi document exista intr-un format mai bogat">· umbrit de ' + esc(d.shadowed_by) + '</span>'
|
? ' <span class="muted" title="acelaşi document exista intr-un format mai bogat">· umbrit de ' + esc(d.shadowed_by) + '</span>'
|
||||||
: '';
|
: '';
|
||||||
return '<tr><td data-label="Nume" class="mono">' + esc(d.name) + umbrit + '</td>' +
|
return '<tr><td data-label="Nume" class="mono">' + esc(d.name) + local + umbrit + '</td>' +
|
||||||
'<td data-label="Mărime" class="num">' + d.size + ' B</td>' +
|
'<td data-label="Mărime" class="num">' + d.size + ' B</td>' +
|
||||||
'<td><button class="small danger" onclick="mDelDoc(\'' + esc(d.name) + '\')">Șterge</button></td></tr>';
|
'<td><button class="small danger" onclick="mDelDoc(\'' + esc(d.name) + '\')">Șterge</button></td></tr>';
|
||||||
}).join('') : '<tr><td colspan="3" class="empty" data-label="">niciun document</td></tr>';
|
}).join('') : '<tr><td colspan="3" class="empty" data-label="">niciun document</td></tr>';
|
||||||
|
|||||||
@@ -197,10 +197,21 @@ puntii Discord — vezi `../discord-bridge/README.md` pentru URL si autentificar
|
|||||||
|
|
||||||
## Depozitul de documente
|
## Depozitul de documente
|
||||||
|
|
||||||
Fisiere `.txt`/`.md`/`.xml` in `~/.maria-bridge/documents/` (lista exacta:
|
Fisiere `.txt`/`.md`/`.xml` (lista exacta: `store.DOC_EXTENSIONS`), in **doua
|
||||||
`store.DOC_EXTENSIONS`). Se pot administra:
|
directoare care formeaza un singur spatiu de nume**:
|
||||||
1. **manual din dashboard-ul comun** (adaugare/stergere text, reindexare automata la salvare);
|
|
||||||
2. **prin sincronizare Google Drive** — vezi mai jos.
|
| Director | Ce e acolo |
|
||||||
|
|---|---|
|
||||||
|
| `~/.maria-bridge/documents/` | **oglinda Google Drive.** `rclone sync` sterge de aici tot ce nu exista in Drive — nu pune nimic de mana. |
|
||||||
|
| `~/.maria-bridge/documents-local/` | documente care **nu** vin din Drive: dictionare proprii, ce se adauga din dashboard. Sincronizarea nu se atinge de ele. |
|
||||||
|
|
||||||
|
Cand acelasi nume apare in ambele, castiga cel din Drive (e sursa comuna a echipei;
|
||||||
|
copia locala ar putea fi o versiune veche uitata acolo). In dashboard, documentele
|
||||||
|
locale sunt marcate „local".
|
||||||
|
|
||||||
|
Directorul local exista fiindca altfel **tot ce se adauga din dashboard traia pana
|
||||||
|
la urmatorul tur de sincronizare** — maximum 10 minute. Acum adaugarea din dashboard
|
||||||
|
scrie direct acolo.
|
||||||
|
|
||||||
## Sincronizare cu Google Drive
|
## Sincronizare cu Google Drive
|
||||||
|
|
||||||
@@ -245,9 +256,9 @@ esuata — reia pasii 1-2.
|
|||||||
Restul din dosar — chatflow-uri Flowise `.json`, scripturi `.ps1`, `.docx` — sunt
|
Restul din dosar — chatflow-uri Flowise `.json`, scripturi `.ps1`, `.docx` — sunt
|
||||||
ignorate deliberat: nu sunt cunostinte de suport.
|
ignorate deliberat: nu sunt cunostinte de suport.
|
||||||
|
|
||||||
**`sync` sterge local ce nu mai exista in Drive**, deci depozitul e o oglinda a
|
**`sync` sterge local ce nu mai exista in Drive**, deci `documents/` e o oglinda a
|
||||||
dosarului din Drive, nu o colectie care creste. Documentele adaugate manual din
|
dosarului din Drive, nu o colectie care creste. De aceea exista `documents-local/`:
|
||||||
dashboard dispar la prima sincronizare daca nu exista si in Drive.
|
ce e adaugat de mana sau din dashboard sta acolo si nu e atins de sincronizare.
|
||||||
|
|
||||||
### Dictionarul de erori Oracle
|
### Dictionarul de erori Oracle
|
||||||
|
|
||||||
@@ -260,10 +271,13 @@ din captura.
|
|||||||
|
|
||||||
Doua lucruri de stiut:
|
Doua lucruri de stiut:
|
||||||
|
|
||||||
- **E samanta, nu sursa.** Fisierul din git nu ajunge singur in index. Depozitul e
|
- **Fisierul din git nu ajunge singur in index.** Copia care ruleaza acum sta in
|
||||||
o oglinda a Drive-ului, deci copia pusa direct in `~/.maria-bridge/documents/`
|
`~/.maria-bridge/documents-local/`, in afara oglinzii Drive. Asa supravietuieste
|
||||||
**dispare la prima sincronizare**. Ca sa ramana, pune-l in `document_store` din
|
sincronizarilor, dar **exista doar pe acest container**. Locul lui pe termen lung
|
||||||
Drive (pe Windows: `D:\GoogleDrive\romfast\document_store`).
|
e `document_store` din Drive (pe Windows: `D:\GoogleDrive\romfast\document_store`),
|
||||||
|
unde il vede si restul echipei si de unde se poate edita fara acces la server.
|
||||||
|
Cand ajunge in Drive, sterge copia locala — altfel raman doua versiuni si castiga
|
||||||
|
tacut cea din Drive.
|
||||||
- **E scris pentru clienti, nu pentru administratori.** Nicio rezolvare nu contine
|
- **E scris pentru clienti, nu pentru administratori.** Nicio rezolvare nu contine
|
||||||
nume de servere, IP-uri, porturi sau pasi de administrare — la erorile care chiar
|
nume de servere, IP-uri, porturi sau pasi de administrare — la erorile care chiar
|
||||||
se rezolva doar tehnic (ORA-01652 spatiu plin, ORA-28000 cont blocat) textul spune
|
se rezolva doar tehnic (ORA-01652 spatiu plin, ORA-28000 cont blocat) textul spune
|
||||||
|
|||||||
@@ -44,6 +44,11 @@ CAZURI: list[tuple[str, bool]] = [
|
|||||||
("cum inregistrez o factura de achizitie in ROA", True),
|
("cum inregistrez o factura de achizitie in ROA", True),
|
||||||
("bilant contabil", True),
|
("bilant contabil", True),
|
||||||
("import de date", True),
|
("import de date", True),
|
||||||
|
# dupa adaugarea dictionarului de erori Oracle (oracle-erori-uzuale.xml)
|
||||||
|
("ORA-01722 invalid number la salvarea facturii", True),
|
||||||
|
("imi da ORA-12154 cand deschid programul", True),
|
||||||
|
("ORA-00001 unique constraint violated", True),
|
||||||
|
("eroare ORA-28000 the account is locked", True),
|
||||||
("care e capitala Frantei", False),
|
("care e capitala Frantei", False),
|
||||||
("cat costa un bilet de avion la Paris", False),
|
("cat costa un bilet de avion la Paris", False),
|
||||||
("imi da eroare la imprimanta HP LaserJet, nu trage hartia", False),
|
("imi da eroare la imprimanta HP LaserJet, nu trage hartia", False),
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ _DEFAULT_DIR = pathlib.Path.home() / ".maria-bridge"
|
|||||||
|
|
||||||
STATE_DIR: pathlib.Path = pathlib.Path(os.environ.get("MARIA_BRIDGE_DIR") or _DEFAULT_DIR)
|
STATE_DIR: pathlib.Path = pathlib.Path(os.environ.get("MARIA_BRIDGE_DIR") or _DEFAULT_DIR)
|
||||||
DOCS_DIR: pathlib.Path = STATE_DIR / "documents"
|
DOCS_DIR: pathlib.Path = STATE_DIR / "documents"
|
||||||
|
# Documente care NU vin din Drive. `rclone sync` sterge din DOCS_DIR tot ce nu
|
||||||
|
# exista in Drive, deci orice fisier pus acolo de mana dispare la urmatorul tur.
|
||||||
|
# Ce sta aici e in afara oglinzii si supravietuieste.
|
||||||
|
DOCS_LOCAL_DIR: pathlib.Path = STATE_DIR / "documents-local"
|
||||||
INDEX_FILE: pathlib.Path = STATE_DIR / "rag_index.json"
|
INDEX_FILE: pathlib.Path = STATE_DIR / "rag_index.json"
|
||||||
LOCK_FILE: pathlib.Path = STATE_DIR / ".rag.lock"
|
LOCK_FILE: pathlib.Path = STATE_DIR / ".rag.lock"
|
||||||
LOG_DIR: pathlib.Path = STATE_DIR / "logs"
|
LOG_DIR: pathlib.Path = STATE_DIR / "logs"
|
||||||
@@ -87,11 +91,12 @@ def parse_env(text: str) -> dict[str, str]:
|
|||||||
|
|
||||||
def reload(base_dir: str | os.PathLike | None = None) -> dict[str, str]:
|
def reload(base_dir: str | os.PathLike | None = None) -> dict[str, str]:
|
||||||
"""Recalculeaza caile si reciteste env-ul. Returneaza dictionarul incarcat."""
|
"""Recalculeaza caile si reciteste env-ul. Returneaza dictionarul incarcat."""
|
||||||
global STATE_DIR, DOCS_DIR, INDEX_FILE, LOCK_FILE, LOG_DIR, ENV_FILE, AUTH_DIR, _env
|
global STATE_DIR, DOCS_DIR, DOCS_LOCAL_DIR, INDEX_FILE, LOCK_FILE, LOG_DIR, ENV_FILE, AUTH_DIR, _env
|
||||||
if base_dir is None:
|
if base_dir is None:
|
||||||
base_dir = os.environ.get("MARIA_BRIDGE_DIR") or _DEFAULT_DIR
|
base_dir = os.environ.get("MARIA_BRIDGE_DIR") or _DEFAULT_DIR
|
||||||
STATE_DIR = pathlib.Path(base_dir)
|
STATE_DIR = pathlib.Path(base_dir)
|
||||||
DOCS_DIR = STATE_DIR / "documents"
|
DOCS_DIR = STATE_DIR / "documents"
|
||||||
|
DOCS_LOCAL_DIR = STATE_DIR / "documents-local"
|
||||||
INDEX_FILE = STATE_DIR / "rag_index.json"
|
INDEX_FILE = STATE_DIR / "rag_index.json"
|
||||||
LOCK_FILE = STATE_DIR / ".rag.lock"
|
LOCK_FILE = STATE_DIR / ".rag.lock"
|
||||||
LOG_DIR = STATE_DIR / "logs"
|
LOG_DIR = STATE_DIR / "logs"
|
||||||
|
|||||||
@@ -4,6 +4,14 @@ Fisiere text (.txt/.md/.xml) sub STATE_DIR/documents/, un singur nivel (fara
|
|||||||
subdirectoare), ca numele afisat in dashboard sa fie neambiguu si sa poata servi
|
subdirectoare), ca numele afisat in dashboard sa fie neambiguu si sa poata servi
|
||||||
direct ca parametru de request fara riscuri de traversare de cale.
|
direct ca parametru de request fara riscuri de traversare de cale.
|
||||||
|
|
||||||
|
DOUA DIRECTOARE, un singur spatiu de nume:
|
||||||
|
documents/ oglinda Google Drive. `rclone sync` STERGE de aici tot ce nu
|
||||||
|
exista in Drive — nu pune nimic de mana.
|
||||||
|
documents-local/ documente care nu vin din Drive (dictionare proprii, ce se
|
||||||
|
adauga din dashboard). Sincronizarea nu se atinge de ele.
|
||||||
|
Cand acelasi nume apare in ambele, castiga cel din Drive: e sursa comuna a
|
||||||
|
echipei, iar copia locala ar putea fi o versiune veche uitata acolo.
|
||||||
|
|
||||||
Cand acelasi document exista in mai multe formate (`d406_saft_knowledge.xml` si
|
Cand acelasi document exista in mai multe formate (`d406_saft_knowledge.xml` si
|
||||||
`d406_saft_knowledge.md`), la indexare intra UNUL SINGUR, cel cu formatul cel mai
|
`d406_saft_knowledge.md`), la indexare intra UNUL SINGUR, cel cu formatul cel mai
|
||||||
bogat: .xml > .md > .txt. Sursele .xml sunt structurate pe probleme
|
bogat: .xml > .md > .txt. Sursele .xml sunt structurate pe probleme
|
||||||
@@ -38,13 +46,32 @@ def validate_name(name: str) -> str:
|
|||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
def _all_files() -> list[pathlib.Path]:
|
||||||
|
"""Fisierele din ambele directoare. La acelasi nume, cel din Drive castiga."""
|
||||||
|
vazute: dict[str, pathlib.Path] = {}
|
||||||
|
for d in (config.DOCS_LOCAL_DIR, config.DOCS_DIR): # Drive ultimul = suprascrie
|
||||||
|
if not d.is_dir():
|
||||||
|
continue
|
||||||
|
for f in sorted(d.glob("*")):
|
||||||
|
if f.is_file() and f.suffix in DOC_EXTENSIONS:
|
||||||
|
vazute[f.name] = f
|
||||||
|
return [vazute[n] for n in sorted(vazute)]
|
||||||
|
|
||||||
|
|
||||||
|
def path_for(name: str) -> pathlib.Path | None:
|
||||||
|
"""Unde e documentul, in oricare din cele doua directoare."""
|
||||||
|
name = validate_name(name)
|
||||||
|
for d in (config.DOCS_DIR, config.DOCS_LOCAL_DIR):
|
||||||
|
p = d / name
|
||||||
|
if p.is_file():
|
||||||
|
return p
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def list_documents() -> list[dict]:
|
def list_documents() -> list[dict]:
|
||||||
"""Tot ce e in depozit. `shadowed_by` spune de ce un fisier nu ajunge in index."""
|
"""Tot ce e in depozit. `shadowed_by` spune de ce un fisier nu ajunge in index."""
|
||||||
config.DOCS_DIR.mkdir(parents=True, exist_ok=True)
|
config.DOCS_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
files = [
|
files = _all_files()
|
||||||
f for f in sorted(config.DOCS_DIR.glob("*"))
|
|
||||||
if f.is_file() and f.suffix in DOC_EXTENSIONS
|
|
||||||
]
|
|
||||||
# castigatorul per nume de baza: formatul cel mai bogat
|
# castigatorul per nume de baza: formatul cel mai bogat
|
||||||
winners: dict[str, str] = {}
|
winners: dict[str, str] = {}
|
||||||
for f in files:
|
for f in files:
|
||||||
@@ -60,6 +87,7 @@ def list_documents() -> list[dict]:
|
|||||||
"name": f.name,
|
"name": f.name,
|
||||||
"size": st.st_size,
|
"size": st.st_size,
|
||||||
"mtime": st.st_mtime,
|
"mtime": st.st_mtime,
|
||||||
|
"local": f.parent == config.DOCS_LOCAL_DIR,
|
||||||
"shadowed_by": None if winner == f.name else winner,
|
"shadowed_by": None if winner == f.name else winner,
|
||||||
})
|
})
|
||||||
return out
|
return out
|
||||||
@@ -71,20 +99,27 @@ def documents_for_index() -> list[dict]:
|
|||||||
|
|
||||||
|
|
||||||
def read_document(name: str) -> str:
|
def read_document(name: str) -> str:
|
||||||
name = validate_name(name)
|
path = path_for(name)
|
||||||
return (config.DOCS_DIR / name).read_text(encoding="utf-8")
|
if path is None:
|
||||||
|
raise FileNotFoundError(name)
|
||||||
|
return path.read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
def write_document(name: str, content: str) -> None:
|
def write_document(name: str, content: str) -> None:
|
||||||
|
"""Scrie in documents-local/, NU in oglinda Drive.
|
||||||
|
|
||||||
|
Un document scris in `documents/` dispare la urmatorul `rclone sync` — asta
|
||||||
|
se intampla pana acum cu tot ce se adauga din dashboard.
|
||||||
|
"""
|
||||||
name = validate_name(name)
|
name = validate_name(name)
|
||||||
config.DOCS_DIR.mkdir(parents=True, exist_ok=True)
|
config.DOCS_LOCAL_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
(config.DOCS_DIR / name).write_text(content, encoding="utf-8")
|
(config.DOCS_LOCAL_DIR / name).write_text(content, encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
def delete_document(name: str) -> bool:
|
def delete_document(name: str) -> bool:
|
||||||
name = validate_name(name)
|
"""Sterge documentul de unde se afla. Cel din Drive revine la sincronizare."""
|
||||||
path = config.DOCS_DIR / name
|
path = path_for(name)
|
||||||
if not path.exists():
|
if path is None:
|
||||||
return False
|
return False
|
||||||
path.unlink()
|
path.unlink()
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -32,10 +32,11 @@ def _fingerprint() -> str:
|
|||||||
doar cand s-a schimbat efectiv ceva, nu la fiecare tur de sincronizare."""
|
doar cand s-a schimbat efectiv ceva, nu la fiecare tur de sincronizare."""
|
||||||
config.DOCS_DIR.mkdir(parents=True, exist_ok=True)
|
config.DOCS_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
h = hashlib.sha256()
|
h = hashlib.sha256()
|
||||||
for f in sorted(config.DOCS_DIR.glob("*")):
|
# amandoua directoarele: si un document local adaugat din dashboard trebuie sa
|
||||||
if f.is_file() and f.suffix in store.DOC_EXTENSIONS:
|
# declanseze reindexarea, nu doar ce vine din Drive
|
||||||
st = f.stat()
|
for f in store._all_files():
|
||||||
h.update(f"{f.name}:{st.st_mtime_ns}:{st.st_size}\n".encode())
|
st = f.stat()
|
||||||
|
h.update(f"{f.name}:{st.st_mtime_ns}:{st.st_size}\n".encode())
|
||||||
return h.hexdigest()
|
return h.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -237,3 +237,57 @@ def test_textul_modificat_se_reembedeaza(monkeypatch, write):
|
|||||||
|
|
||||||
assert calculate == ["varianta modificata"]
|
assert calculate == ["varianta modificata"]
|
||||||
assert rezultat["embeddings_refolosite"] == 0
|
assert rezultat["embeddings_refolosite"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
# --- documente locale, in afara oglinzii Drive -------------------------------
|
||||||
|
|
||||||
|
def _scrie_local(name: str, text: str):
|
||||||
|
import config
|
||||||
|
config.DOCS_LOCAL_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
p = config.DOCS_LOCAL_DIR / name
|
||||||
|
p.write_text(text, encoding="utf-8")
|
||||||
|
return p
|
||||||
|
|
||||||
|
|
||||||
|
def test_documentele_locale_intra_in_index(write):
|
||||||
|
import store
|
||||||
|
write("din-drive.md", "vine din Drive")
|
||||||
|
_scrie_local("al-meu.md", "adaugat local")
|
||||||
|
|
||||||
|
nume = {d["name"] for d in store.documents_for_index()}
|
||||||
|
assert nume == {"din-drive.md", "al-meu.md"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_se_vede_care_e_local(write):
|
||||||
|
import store
|
||||||
|
write("din-drive.md", "x")
|
||||||
|
_scrie_local("al-meu.md", "y")
|
||||||
|
dupa_nume = {d["name"]: d for d in store.list_documents()}
|
||||||
|
assert dupa_nume["al-meu.md"]["local"] is True
|
||||||
|
assert dupa_nume["din-drive.md"]["local"] is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_la_acelasi_nume_castiga_versiunea_din_drive(write):
|
||||||
|
import store
|
||||||
|
_scrie_local("acelasi.md", "copie locala veche")
|
||||||
|
write("acelasi.md", "versiunea din Drive")
|
||||||
|
assert store.read_document("acelasi.md") == "versiunea din Drive"
|
||||||
|
assert len(store.list_documents()) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_scrierea_din_dashboard_merge_in_directorul_local():
|
||||||
|
# Pana acum ajungea in oglinda Drive si disparea la urmatorul rclone sync.
|
||||||
|
import config
|
||||||
|
import store
|
||||||
|
store.write_document("nota.md", "ceva de tinut minte")
|
||||||
|
assert (config.DOCS_LOCAL_DIR / "nota.md").exists()
|
||||||
|
assert not (config.DOCS_DIR / "nota.md").exists()
|
||||||
|
|
||||||
|
|
||||||
|
def test_stergerea_gaseste_documentul_in_ambele_directoare(write):
|
||||||
|
import store
|
||||||
|
_scrie_local("local.md", "x")
|
||||||
|
write("drive.md", "y")
|
||||||
|
assert store.delete_document("local.md") is True
|
||||||
|
assert store.delete_document("drive.md") is True
|
||||||
|
assert store.delete_document("inexistent.md") is False
|
||||||
|
|||||||
Reference in New Issue
Block a user