feat: visor da peça com zoom real, ficha NOBRADE e filtros agrupados pela norma
- painel à esquerda (folha no celular) com barra ← mural | filtros, palco com roda, pinça, duplo clique, arrastar e modo só a imagem (F) - ficha arquivística: código, nível, arranjo clicável, data da norma com selo de atribuída, produtor ISAAR, suporte, original, reprodução e citação - filtros em Arranjo, Data, Pontos de acesso e Suporte e uso; autor pela autoridade, espécie da 1.5 e condição de reprodução; link pelo código - datas: ano implausível vira ilegível, atribuição pelo miolo p10–p90 e histograma só com datas de até 30 anos de amplitude
This commit is contained in:
@@ -21,6 +21,7 @@ from __future__ import annotations
|
|||||||
import re
|
import re
|
||||||
import unicodedata
|
import unicodedata
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
MESES = {
|
MESES = {
|
||||||
'jan': 1, 'janeiro': 1, 'fev': 2, 'fevereiro': 2, 'mar': 3, 'março': 3, 'marco': 3,
|
'jan': 1, 'janeiro': 1, 'fev': 2, 'fevereiro': 2, 'mar': 3, 'março': 3, 'marco': 3,
|
||||||
@@ -247,6 +248,20 @@ def _intervalo(s: str) -> tuple[Ponto | None, Ponto | None, bool]:
|
|||||||
return None, None, False
|
return None, None, False
|
||||||
|
|
||||||
|
|
||||||
|
ANO_MIN, ANO_MAX = 1500, date.today().year
|
||||||
|
|
||||||
|
|
||||||
|
def _plausivel(d: Data) -> Data:
|
||||||
|
"""Um ano fora do possível ("20-12-1395") é erro de digitação na origem: não vira data."""
|
||||||
|
anos = [x for x in d.anos() if x is not None]
|
||||||
|
if anos and (min(anos) < ANO_MIN or max(anos) > ANO_MAX):
|
||||||
|
d.notas.append(f'ano implausível na origem ({d.original}); data desconsiderada')
|
||||||
|
d.inicio_p = d.fim_p = None
|
||||||
|
d.relacao = ''
|
||||||
|
d.motivo = 'ilegível'
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
def interpreta(texto: str | None) -> Data:
|
def interpreta(texto: str | None) -> Data:
|
||||||
original = (texto or '').strip()
|
original = (texto or '').strip()
|
||||||
d = Data(original)
|
d = Data(original)
|
||||||
@@ -311,11 +326,11 @@ def interpreta(texto: str | None) -> Data:
|
|||||||
p, inc = _ponto(s)
|
p, inc = _ponto(s)
|
||||||
if p:
|
if p:
|
||||||
d.inicio_p, d.incerta = p, d.incerta or inc
|
d.inicio_p, d.incerta = p, d.incerta or inc
|
||||||
return d
|
return _plausivel(d)
|
||||||
a, b, inc = _intervalo(s)
|
a, b, inc = _intervalo(s)
|
||||||
if a:
|
if a:
|
||||||
d.inicio_p, d.fim_p, d.incerta = a, b, d.incerta or inc
|
d.inicio_p, d.fim_p, d.incerta = a, b, d.incerta or inc
|
||||||
return d
|
return _plausivel(d)
|
||||||
# "1900-" (colchete aberto): dali em diante
|
# "1900-" (colchete aberto): dali em diante
|
||||||
if m := re.fullmatch(r'(\d{4})\s*-', s):
|
if m := re.fullmatch(r'(\d{4})\s*-', s):
|
||||||
d.inicio_p, d.relacao = Ponto(int(m[1])), 'depois'
|
d.inicio_p, d.relacao = Ponto(int(m[1])), 'depois'
|
||||||
|
|||||||
@@ -122,6 +122,15 @@ def _limites(ds: list[dict]) -> dict:
|
|||||||
'inicio': a, 'fim': b, 'qualidade': 'datas-limite', 'tipo': 'produção'}
|
'inicio': a, 'fim': b, 'qualidade': 'datas-limite', 'tipo': 'produção'}
|
||||||
|
|
||||||
|
|
||||||
|
def _robustos(ds: list[dict]) -> dict:
|
||||||
|
"""Datas-limite para atribuir a dossiês sem data: o miolo (percentis 10–90)
|
||||||
|
das datas conhecidas, para que um punhado de extremos não vire [1800-1999?]."""
|
||||||
|
anos = sorted(d['inicio'] for d in ds if d and d.get('inicio'))
|
||||||
|
if not anos:
|
||||||
|
return {'inicio': None, 'fim': None}
|
||||||
|
return {'inicio': anos[int(len(anos) * .1)], 'fim': anos[min(len(anos) - 1, int(len(anos) * .9))]}
|
||||||
|
|
||||||
|
|
||||||
def _dimensao(especies: Counter, n_itens: int, bytes_: int) -> dict:
|
def _dimensao(especies: Counter, n_itens: int, bytes_: int) -> dict:
|
||||||
partes = [f'{n} {e}' for e, n in especies.most_common()]
|
partes = [f'{n} {e}' for e, n in especies.most_common()]
|
||||||
return {'por_especie': dict(especies), 'arquivos_digitais': n_itens, 'bytes': bytes_,
|
return {'por_especie': dict(especies), 'arquivos_digitais': n_itens, 'bytes': bytes_,
|
||||||
@@ -330,9 +339,9 @@ def monta():
|
|||||||
|
|
||||||
# datas atribuídas aos s.d. (proc. 1.3.3.2): período do fotógrafo, senão datas-limite da série
|
# datas atribuídas aos s.d. (proc. 1.3.3.2): período do fotógrafo, senão datas-limite da série
|
||||||
rev_por_handle = {r['handle']: r for r in rev_datas}
|
rev_por_handle = {r['handle']: r for r in rev_datas}
|
||||||
lim_secao = {inst: _limites([u['1.3']['producao'] for (i, _), us in por_serie.items() if i == inst
|
lim_secao = {inst: _robustos([u['1.3']['producao'] for (i, _), us in por_serie.items() if i == inst
|
||||||
for u in us if not u['_sd']]) for inst, _ in por_serie}
|
for u in us if not u['_sd']]) for inst, _ in por_serie}
|
||||||
lim_serie = {k: _limites([u['1.3']['producao'] for u in us if not u['_sd']]) for k, us in por_serie.items()}
|
lim_serie = {k: _robustos([u['1.3']['producao'] for u in us if not u['_sd']]) for k, us in por_serie.items()}
|
||||||
for k, us in por_serie.items():
|
for k, us in por_serie.items():
|
||||||
for u in us:
|
for u in us:
|
||||||
if not u['_sd']:
|
if not u['_sd']:
|
||||||
@@ -342,10 +351,10 @@ def monta():
|
|||||||
if per:
|
if per:
|
||||||
nova = datas.atribuida(*per, 'período de atividade do autor')
|
nova = datas.atribuida(*per, 'período de atividade do autor')
|
||||||
elif lim_serie[k]['inicio']:
|
elif lim_serie[k]['inicio']:
|
||||||
nova = datas.atribuida(lim_serie[k]['inicio'], lim_serie[k]['fim'], 'datas-limite da série')
|
nova = datas.atribuida(lim_serie[k]['inicio'], lim_serie[k]['fim'], 'miolo das datas conhecidas da série (percentis 10–90)')
|
||||||
elif lim_secao[k[0]]['inicio']:
|
elif lim_secao[k[0]]['inicio']:
|
||||||
nova = datas.atribuida(lim_secao[k[0]]['inicio'], lim_secao[k[0]]['fim'],
|
nova = datas.atribuida(lim_secao[k[0]]['inicio'], lim_secao[k[0]]['fim'],
|
||||||
'datas-limite da instituição de origem')
|
'miolo das datas conhecidas da instituição de origem (percentis 10–90)')
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
nova['original'] = u['1.3']['producao']['original']
|
nova['original'] = u['1.3']['producao']['original']
|
||||||
|
|||||||
@@ -8,15 +8,28 @@ Campos novos em meta (curtos: o mapa inteiro desce para o navegador):
|
|||||||
ref código de referência (1.1) dt data na forma da NOBRADE (1.3)
|
ref código de referência (1.1) dt data na forma da NOBRADE (1.3)
|
||||||
ano ano inicial, para o histograma si índice em `series` do mapa (nível 3)
|
ano ano inicial, para o histograma si índice em `series` do mapa (nível 3)
|
||||||
sup espécie e suporte (1.5) rep categoria de reprodução (4.2)
|
sup espécie e suporte (1.5) rep categoria de reprodução (4.2)
|
||||||
|
ai índice em `autoridades` (2.1, forma autorizada ISAAR(CPF))
|
||||||
|
dq 1 quando a data foi atribuída pelo arquivista (proc. 1.3.3.2)
|
||||||
|
No topo do mapa: `series`, `autoridades`, `instituicoes` (community → nome
|
||||||
|
autorizado) e `colecao` (código e título do nível 1).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from .config import RAIZ, SAIDA
|
from .config import COLECAO_TITULO, INSTITUICOES, RAIZ, SAIDA, codigo
|
||||||
|
|
||||||
MAPA = RAIZ / 'web' / 'atlas' / 'atlas-map.json'
|
MAPA = RAIZ / 'web' / 'atlas' / 'atlas-map.json'
|
||||||
|
|
||||||
|
|
||||||
|
def ano(p: dict):
|
||||||
|
"""O ano que posiciona a peça no histograma — só quando a data é precisa o
|
||||||
|
bastante (até 30 anos). "[18--]" ou "[1880-1930?]" ficam na ficha, fora do filtro."""
|
||||||
|
i, f = p.get('inicio'), p.get('fim')
|
||||||
|
if i and f and f - i <= 30:
|
||||||
|
return i
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
series, dossies = {}, {}
|
series, dossies = {}, {}
|
||||||
for linha in (SAIDA / 'unidades.jsonl').open(encoding='utf-8'):
|
for linha in (SAIDA / 'unidades.jsonl').open(encoding='utf-8'):
|
||||||
@@ -30,6 +43,11 @@ def main():
|
|||||||
titulos = sorted(set(series.values()), key=str.casefold)
|
titulos = sorted(set(series.values()), key=str.casefold)
|
||||||
idx = {t: i for i, t in enumerate(titulos)}
|
idx = {t: i for i, t in enumerate(titulos)}
|
||||||
d['series'] = titulos
|
d['series'] = titulos
|
||||||
|
formas = sorted({p['forma'] for u in dossies.values() for p in u['2.1'] if p.get('id')}, key=str.casefold)
|
||||||
|
ai = {f: i for i, f in enumerate(formas)}
|
||||||
|
d['autoridades'] = formas
|
||||||
|
d['instituicoes'] = {k: v[1] for k, v in INSTITUICOES.items()}
|
||||||
|
d['colecao'] = {'codigo': codigo('BF'), 'titulo': COLECAO_TITULO}
|
||||||
feitos = 0
|
feitos = 0
|
||||||
for h, e in d['map'].items():
|
for h, e in d['map'].items():
|
||||||
u = dossies.get(h)
|
u = dossies.get(h)
|
||||||
@@ -39,9 +57,18 @@ def main():
|
|||||||
dim = u['1.5']
|
dim = u['1.5']
|
||||||
sup = dim['especie'] + (f" {dim['cor']}" if dim.get('cor') else '') + (f", {dim['suporte']}" if dim.get('suporte') else '')
|
sup = dim['especie'] + (f" {dim['cor']}" if dim.get('cor') else '') + (f", {dim['suporte']}" if dim.get('suporte') else '')
|
||||||
e['meta'].update({
|
e['meta'].update({
|
||||||
'ref': u['1.1'], 'dt': p.get('forma'), 'ano': p.get('inicio') or p.get('fim'),
|
'ref': u['1.1'], 'dt': p.get('forma'), 'ano': ano(p),
|
||||||
'si': idx[series[u['pai']]], 'sup': sup, 'rep': u['4.2']['categoria'],
|
'si': idx[series[u['pai']]], 'sup': sup, 'rep': u['4.2']['categoria'],
|
||||||
})
|
})
|
||||||
|
aut = next((p['forma'] for p in u['2.1'] if p.get('id')), None)
|
||||||
|
if e['meta'].get('ano') is None:
|
||||||
|
e['meta'].pop('ano', None)
|
||||||
|
e['meta'].pop('ai', None)
|
||||||
|
e['meta'].pop('dq', None)
|
||||||
|
if aut:
|
||||||
|
e['meta']['ai'] = ai[aut]
|
||||||
|
if (p.get('notas') or [''])[0].startswith('data atribuída pelo arquivista'):
|
||||||
|
e['meta']['dq'] = 1
|
||||||
feitos += 1
|
feitos += 1
|
||||||
MAPA.write_text(json.dumps(d, ensure_ascii=False, separators=(',', ':')), encoding='utf-8')
|
MAPA.write_text(json.dumps(d, ensure_ascii=False, separators=(',', ':')), encoding='utf-8')
|
||||||
print(f'{feitos} de {len(d["map"])} peças com descrição NOBRADE; {len(titulos)} séries')
|
print(f'{feitos} de {len(d["map"])} peças com descrição NOBRADE; {len(titulos)} séries')
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+605
-299
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user