We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Migração de documentos
Foram retiradas do arquivo sapl/utils.py as duas funções abaixo no seguinte PR
https://github.com/interlegis/sapl/commit/71491c33dd3769b66bc98199e6d8027f6862e4e5#diff-248fb73d01307eb54567bfe0ba11d306

def save_texto(sender, instance, **kwargs):
    update_index = UpdateIndexCommand()
    update_index.start()


def delete_texto(sender, instance, **kwargs):
    update_index = UpdateIndexCommand()
    update_index.start()

No entanto o migrador de documentos precisa delas
https://github.com/interlegis/sapl/blob/master/sapl/legacy/migracao_documentos.py#L17

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `sapl/legacy/migracao_documentos.py`
Content:
```
1 import mimetypes
2 import os
3 import re
4 
5 import magic
6 from django.db.models.signals import post_delete, post_save
7 
8 from sapl.base.models import CasaLegislativa
9 from sapl.materia.models import (DocumentoAcessorio, MateriaLegislativa,
10                                  Proposicao)
11 from sapl.norma.models import NormaJuridica
12 from sapl.parlamentares.models import Parlamentar
13 from sapl.protocoloadm.models import (DocumentoAcessorioAdministrativo,
14                                       DocumentoAdministrativo)
15 from sapl.sessao.models import SessaoPlenaria
16 from sapl.settings import MEDIA_ROOT
17 from sapl.utils import delete_texto, save_texto
18 
19 # MIGRAÇÃO DE DOCUMENTOS  ###################################################
20 EXTENSOES = {
21     'application/msword': '.doc',
22     'application/pdf': '.pdf',
23     'application/vnd.oasis.opendocument.text': '.odt',
24     'application/vnd.openxmlformats-officedocument.wordprocessingml.document': '.docx',  # noqa
25     'application/xml': '.xml',
26     'application/zip': '.zip',
27     'image/jpeg': '.jpeg',
28     'image/png': '.png',
29     'text/html': '.html',
30     'text/rtf': '.rtf',
31     'text/x-python': '.py',
32     'text/plain': '.ksh',
33     'text/plain': '.c',
34     'text/plain': '.h',
35     'text/plain': '.txt',
36     'text/plain': '.bat',
37     'text/plain': '.pl',
38     'text/plain': '.asc',
39     'text/plain': '.text',
40     'text/plain': '.pot',
41     'text/plain': '.brf',
42     'text/plain': '.srt',
43 
44     # sem extensao
45     'application/octet-stream': '',  # binário
46     'inode/x-empty': '',  # vazio
47 }
48 
49 DOCS = {
50     CasaLegislativa: [(
51         'logotipo',
52         'props_sapl/logo_casa.gif',
53         'casa/logotipo/logo_casa.gif')],
54     Parlamentar: [(
55         'fotografia',
56         'parlamentar/fotos/{}_foto_parlamentar',
57         'public/parlamentar/{0}/{0}_foto_parlamentar{1}')],
58     MateriaLegislativa: [(
59         'texto_original',
60         'materia/{}_texto_integral',
61         'public/materialegislativa/{2}/{0}/{0}_texto_integral{1}')],
62     DocumentoAcessorio: [(
63         'arquivo',
64         'materia/{}',
65         'public/documentoacessorio/{2}/{0}/{0}{1}')],
66     NormaJuridica: [(
67         'texto_integral',
68         'norma_juridica/{}_texto_integral',
69         'public/normajuridica/{2}/{0}/{0}_texto_integral{1}')],
70     SessaoPlenaria: [
71         ('upload_ata',
72          'ata_sessao/{}_ata_sessao',
73          'public/sessaoplenaria/{0}/ata/{0}_ata_sessao{1}'),
74         ('upload_anexo',
75          'anexo_sessao/{}_texto_anexado',
76          'public/sessaoplenaria/{0}/anexo/{0}_texto_anexado{1}')
77     ],
78     Proposicao: [(
79         'texto_original',
80         'proposicao/{}',
81         'private/proposicao/{0}/{0}{1}')],
82     DocumentoAdministrativo: [(
83         'texto_integral',
84         'administrativo/{}_texto_integral',
85         'private/documentoadministrativo/{0}/{0}_texto_integral{1}')
86     ],
87     DocumentoAcessorioAdministrativo: [(
88         'arquivo',
89         'administrativo/{}',
90         'private/documentoacessorioadministrativo/{0}/{0}_acessorio_administrativo{1}')
91     ],
92 }
93 
94 DOCS = {tipo: [(campo,
95                 os.path.join('sapl_documentos', origem),
96                 os.path.join('sapl', destino))
97                for campo, origem, destino in campos]
98         for tipo, campos in DOCS.items()}
99 
100 
101 def em_media(caminho):
102     return os.path.join(MEDIA_ROOT, caminho)
103 
104 
105 def mover_documento(origem, destino):
106     origem, destino = [em_media(c) if not os.path.isabs(c) else c
107                        for c in (origem, destino)]
108     os.makedirs(os.path.dirname(destino), exist_ok=True)
109     os.rename(origem, destino)
110 
111 
112 def get_casa_legislativa():
113     casa = CasaLegislativa.objects.first()
114     if not casa:
115         casa = CasaLegislativa.objects.create(**{k: 'PREENCHER...' for k in [
116             'codigo', 'nome', 'sigla', 'endereco', 'cep', 'municipio', 'uf',
117         ]})
118     return casa
119 
120 
121 def migrar_docs_logo():
122     print('#### Migrando logotipo da casa ####')
123     [(_, origem, destino)] = DOCS[CasaLegislativa]
124     props_sapl = os.path.dirname(origem)
125 
126     # a pasta props_sapl deve conter apenas o origem e metadatas!
127     # Edit: Aparentemente há diretório que contém properties ao invés de
128     # metadata. O assert foi modificado para essa situação.
129     assert set(os.listdir(em_media(props_sapl))) < {
130         'logo_casa.gif', '.metadata', 'logo_casa.gif.metadata',
131         '.properties', 'logo_casa.gif.properties', '.objects'}
132 
133     mover_documento(origem, destino)
134     casa = get_casa_legislativa()
135     casa.logotipo = destino
136     casa.save()
137 
138 
139 def get_extensao(caminho):
140     mime = magic.from_file(caminho, mime=True)
141     try:
142         return EXTENSOES[mime]
143     except KeyError as e:
144         raise Exception('\n'.join([
145             'Extensão não conhecida para o arquivo:',
146             caminho,
147             'E mimetype:',
148             mime,
149             ' Algumas possibilidades são:', ] +
150             ["    '{}': '{}',".format(mime, ext)
151              for ext in mimetypes.guess_all_extensions(mime)] +
152             ['Atualize o código do dicionário EXTENSOES!']
153         )) from e
154 
155 
156 def migrar_docs_por_ids(tipo):
157     for campo, base_origem, base_destino in DOCS[tipo]:
158         print('#### Migrando {} de {} ####'.format(campo, tipo.__name__))
159 
160         dir_origem, nome_origem = os.path.split(em_media(base_origem))
161         pat = re.compile('^{}$'.format(nome_origem.format('(\d+)')))
162 
163         if not os.path.isdir(dir_origem):
164             print('  >>> O diretório {} não existe! Abortado.'.format(
165                 dir_origem))
166             continue
167 
168         for arq in os.listdir(dir_origem):
169             match = pat.match(arq)
170             if match:
171                 # associa documento ao objeto
172                 try:
173                     origem = os.path.join(dir_origem, match.group(0))
174                     id = match.group(1)
175                     obj = tipo.objects.get(pk=id)
176 
177                     extensao = get_extensao(origem)
178                     if hasattr(obj, "ano"):
179                         destino = base_destino.format(id, extensao, obj.ano)
180                     elif isinstance(obj, DocumentoAcessorio):
181                         destino = base_destino.format(
182                             id, extensao, obj.materia.ano)
183                     else:
184                         destino = base_destino.format(id, extensao)
185                     mover_documento(origem, destino)
186 
187                     setattr(obj, campo, destino)
188                     obj.save()
189                 except tipo.DoesNotExist:
190                     msg = '  {} (pk={}) não encontrado para documento em [{}]'
191                     print(msg.format(
192                         tipo.__name__, id, destino))
193 
194 
195 def desconecta_sinais_indexacao():
196     post_save.disconnect(save_texto, NormaJuridica)
197     post_save.disconnect(save_texto, DocumentoAcessorio)
198     post_save.disconnect(save_texto, MateriaLegislativa)
199     post_delete.disconnect(delete_texto, NormaJuridica)
200     post_delete.disconnect(delete_texto, DocumentoAcessorio)
201     post_delete.disconnect(delete_texto, MateriaLegislativa)
202 
203 
204 def conecta_sinais_indexacao():
205     post_save.connect(save_texto, NormaJuridica)
206     post_save.connect(save_texto, DocumentoAcessorio)
207     post_save.connect(save_texto, MateriaLegislativa)
208     post_delete.connect(delete_texto, NormaJuridica)
209     post_delete.connect(delete_texto, DocumentoAcessorio)
210     post_delete.connect(delete_texto, MateriaLegislativa)
211 
212 
213 def migrar_documentos():
214     # precisamos excluir os sinais de post_save e post_delete para não que o
215     # computador não trave com a criação de threads desnecessárias
216     desconecta_sinais_indexacao()
217 
218     # aqui supomos que uma pasta chamada sapl_documentos está em MEDIA_ROOT
219     # com o conteúdo da pasta de mesmo nome do zope
220     # Os arquivos da pasta serão movidos para a nova estrutura e a pasta será
221     # apagada
222     migrar_docs_logo()
223     for tipo in [
224         Parlamentar,
225         MateriaLegislativa,
226         DocumentoAcessorio,
227         NormaJuridica,
228         SessaoPlenaria,
229         Proposicao,
230         DocumentoAdministrativo,
231       	DocumentoAcessorioAdministrativo,
232     ]:
233         migrar_docs_por_ids(tipo)
234 
235     sobrando = [os.path.join(dir, file)
236                 for (dir, _, files) in os.walk(em_media('sapl_documentos'))
237                 for file in files]
238     if sobrando:
239         print('\n#### Encerrado ####\n\n'
240               '{} documentos sobraram sem ser migrados!!!'.format(
241                   len(sobrando)))
242         for doc in sobrando:
243             print('  {}'. format(doc))
244             #
245     # reconexão dos sinais desligados no inicio da migração de documentos
246     conecta_sinais_indexacao()
247
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/sapl/legacy/migracao_documentos.py b/sapl/legacy/migracao_documentos.py
--- a/sapl/legacy/migracao_documentos.py
+++ b/sapl/legacy/migracao_documentos.py
@@ -14,7 +14,6 @@
                                       DocumentoAdministrativo)
 from sapl.sessao.models import SessaoPlenaria
 from sapl.settings import MEDIA_ROOT
-from sapl.utils import delete_texto, save_texto
 
 # MIGRAÇÃO DE DOCUMENTOS  ###################################################
 EXTENSOES = {
@@ -192,29 +191,7 @@
                         tipo.__name__, id, destino))
 
 
-def desconecta_sinais_indexacao():
-    post_save.disconnect(save_texto, NormaJuridica)
-    post_save.disconnect(save_texto, DocumentoAcessorio)
-    post_save.disconnect(save_texto, MateriaLegislativa)
-    post_delete.disconnect(delete_texto, NormaJuridica)
-    post_delete.disconnect(delete_texto, DocumentoAcessorio)
-    post_delete.disconnect(delete_texto, MateriaLegislativa)
-
-
-def conecta_sinais_indexacao():
-    post_save.connect(save_texto, NormaJuridica)
-    post_save.connect(save_texto, DocumentoAcessorio)
-    post_save.connect(save_texto, MateriaLegislativa)
-    post_delete.connect(delete_texto, NormaJuridica)
-    post_delete.connect(delete_texto, DocumentoAcessorio)
-    post_delete.connect(delete_texto, MateriaLegislativa)
-
-
 def migrar_documentos():
-    # precisamos excluir os sinais de post_save e post_delete para não que o
-    # computador não trave com a criação de threads desnecessárias
-    desconecta_sinais_indexacao()
-
     # aqui supomos que uma pasta chamada sapl_documentos está em MEDIA_ROOT
     # com o conteúdo da pasta de mesmo nome do zope
     # Os arquivos da pasta serão movidos para a nova estrutura e a pasta será
@@ -241,6 +218,3 @@
                   len(sobrando)))
         for doc in sobrando:
             print('  {}'. format(doc))
-            #
-    # reconexão dos sinais desligados no inicio da migração de documentos
-    conecta_sinais_indexacao()

{"golden_diff": "diff --git a/sapl/legacy/migracao_documentos.py b/sapl/legacy/migracao_documentos.py\n--- a/sapl/legacy/migracao_documentos.py\n+++ b/sapl/legacy/migracao_documentos.py\n@@ -14,7 +14,6 @@\n                                       DocumentoAdministrativo)\n from sapl.sessao.models import SessaoPlenaria\n from sapl.settings import MEDIA_ROOT\n-from sapl.utils import delete_texto, save_texto\n \n # MIGRA\u00c7\u00c3O DE DOCUMENTOS  ###################################################\n EXTENSOES = {\n@@ -192,29 +191,7 @@\n                         tipo.__name__, id, destino))\n \n \n-def desconecta_sinais_indexacao():\n-    post_save.disconnect(save_texto, NormaJuridica)\n-    post_save.disconnect(save_texto, DocumentoAcessorio)\n-    post_save.disconnect(save_texto, MateriaLegislativa)\n-    post_delete.disconnect(delete_texto, NormaJuridica)\n-    post_delete.disconnect(delete_texto, DocumentoAcessorio)\n-    post_delete.disconnect(delete_texto, MateriaLegislativa)\n-\n-\n-def conecta_sinais_indexacao():\n-    post_save.connect(save_texto, NormaJuridica)\n-    post_save.connect(save_texto, DocumentoAcessorio)\n-    post_save.connect(save_texto, MateriaLegislativa)\n-    post_delete.connect(delete_texto, NormaJuridica)\n-    post_delete.connect(delete_texto, DocumentoAcessorio)\n-    post_delete.connect(delete_texto, MateriaLegislativa)\n-\n-\n def migrar_documentos():\n-    # precisamos excluir os sinais de post_save e post_delete para n\u00e3o que o\n-    # computador n\u00e3o trave com a cria\u00e7\u00e3o de threads desnecess\u00e1rias\n-    desconecta_sinais_indexacao()\n-\n     # aqui supomos que uma pasta chamada sapl_documentos est\u00e1 em MEDIA_ROOT\n     # com o conte\u00fado da pasta de mesmo nome do zope\n     # Os arquivos da pasta ser\u00e3o movidos para a nova estrutura e a pasta ser\u00e1\n@@ -241,6 +218,3 @@\n                   len(sobrando)))\n         for doc in sobrando:\n             print('  {}'. format(doc))\n-            #\n-    # reconex\u00e3o dos sinais desligados no inicio da migra\u00e7\u00e3o de documentos\n-    conecta_sinais_indexacao()\n", "issue": "Migra\u00e7\u00e3o de documentos\nForam retiradas do arquivo sapl/utils.py as duas fun\u00e7\u00f5es abaixo no seguinte PR\r\nhttps://github.com/interlegis/sapl/commit/71491c33dd3769b66bc98199e6d8027f6862e4e5#diff-248fb73d01307eb54567bfe0ba11d306\r\n\r\ndef save_texto(sender, instance, **kwargs):\r\n    update_index = UpdateIndexCommand()\r\n    update_index.start()\r\n\r\n\r\ndef delete_texto(sender, instance, **kwargs):\r\n    update_index = UpdateIndexCommand()\r\n    update_index.start()\r\n\r\nNo entanto o migrador de documentos precisa delas\r\nhttps://github.com/interlegis/sapl/blob/master/sapl/legacy/migracao_documentos.py#L17\n", "before_files": [{"content": "import mimetypes\nimport os\nimport re\n\nimport magic\nfrom django.db.models.signals import post_delete, post_save\n\nfrom sapl.base.models import CasaLegislativa\nfrom sapl.materia.models import (DocumentoAcessorio, MateriaLegislativa,\n                                 Proposicao)\nfrom sapl.norma.models import NormaJuridica\nfrom sapl.parlamentares.models import Parlamentar\nfrom sapl.protocoloadm.models import (DocumentoAcessorioAdministrativo,\n                                      DocumentoAdministrativo)\nfrom sapl.sessao.models import SessaoPlenaria\nfrom sapl.settings import MEDIA_ROOT\nfrom sapl.utils import delete_texto, save_texto\n\n# MIGRA\u00c7\u00c3O DE DOCUMENTOS  ###################################################\nEXTENSOES = {\n    'application/msword': '.doc',\n    'application/pdf': '.pdf',\n    'application/vnd.oasis.opendocument.text': '.odt',\n    'application/vnd.openxmlformats-officedocument.wordprocessingml.document': '.docx',  # noqa\n    'application/xml': '.xml',\n    'application/zip': '.zip',\n    'image/jpeg': '.jpeg',\n    'image/png': '.png',\n    'text/html': '.html',\n    'text/rtf': '.rtf',\n    'text/x-python': '.py',\n    'text/plain': '.ksh',\n    'text/plain': '.c',\n    'text/plain': '.h',\n    'text/plain': '.txt',\n    'text/plain': '.bat',\n    'text/plain': '.pl',\n    'text/plain': '.asc',\n    'text/plain': '.text',\n    'text/plain': '.pot',\n    'text/plain': '.brf',\n    'text/plain': '.srt',\n\n    # sem extensao\n    'application/octet-stream': '',  # bin\u00e1rio\n    'inode/x-empty': '',  # vazio\n}\n\nDOCS = {\n    CasaLegislativa: [(\n        'logotipo',\n        'props_sapl/logo_casa.gif',\n        'casa/logotipo/logo_casa.gif')],\n    Parlamentar: [(\n        'fotografia',\n        'parlamentar/fotos/{}_foto_parlamentar',\n        'public/parlamentar/{0}/{0}_foto_parlamentar{1}')],\n    MateriaLegislativa: [(\n        'texto_original',\n        'materia/{}_texto_integral',\n        'public/materialegislativa/{2}/{0}/{0}_texto_integral{1}')],\n    DocumentoAcessorio: [(\n        'arquivo',\n        'materia/{}',\n        'public/documentoacessorio/{2}/{0}/{0}{1}')],\n    NormaJuridica: [(\n        'texto_integral',\n        'norma_juridica/{}_texto_integral',\n        'public/normajuridica/{2}/{0}/{0}_texto_integral{1}')],\n    SessaoPlenaria: [\n        ('upload_ata',\n         'ata_sessao/{}_ata_sessao',\n         'public/sessaoplenaria/{0}/ata/{0}_ata_sessao{1}'),\n        ('upload_anexo',\n         'anexo_sessao/{}_texto_anexado',\n         'public/sessaoplenaria/{0}/anexo/{0}_texto_anexado{1}')\n    ],\n    Proposicao: [(\n        'texto_original',\n        'proposicao/{}',\n        'private/proposicao/{0}/{0}{1}')],\n    DocumentoAdministrativo: [(\n        'texto_integral',\n        'administrativo/{}_texto_integral',\n        'private/documentoadministrativo/{0}/{0}_texto_integral{1}')\n    ],\n    DocumentoAcessorioAdministrativo: [(\n        'arquivo',\n        'administrativo/{}',\n        'private/documentoacessorioadministrativo/{0}/{0}_acessorio_administrativo{1}')\n    ],\n}\n\nDOCS = {tipo: [(campo,\n                os.path.join('sapl_documentos', origem),\n                os.path.join('sapl', destino))\n               for campo, origem, destino in campos]\n        for tipo, campos in DOCS.items()}\n\n\ndef em_media(caminho):\n    return os.path.join(MEDIA_ROOT, caminho)\n\n\ndef mover_documento(origem, destino):\n    origem, destino = [em_media(c) if not os.path.isabs(c) else c\n                       for c in (origem, destino)]\n    os.makedirs(os.path.dirname(destino), exist_ok=True)\n    os.rename(origem, destino)\n\n\ndef get_casa_legislativa():\n    casa = CasaLegislativa.objects.first()\n    if not casa:\n        casa = CasaLegislativa.objects.create(**{k: 'PREENCHER...' for k in [\n            'codigo', 'nome', 'sigla', 'endereco', 'cep', 'municipio', 'uf',\n        ]})\n    return casa\n\n\ndef migrar_docs_logo():\n    print('#### Migrando logotipo da casa ####')\n    [(_, origem, destino)] = DOCS[CasaLegislativa]\n    props_sapl = os.path.dirname(origem)\n\n    # a pasta props_sapl deve conter apenas o origem e metadatas!\n    # Edit: Aparentemente h\u00e1 diret\u00f3rio que cont\u00e9m properties ao inv\u00e9s de\n    # metadata. O assert foi modificado para essa situa\u00e7\u00e3o.\n    assert set(os.listdir(em_media(props_sapl))) < {\n        'logo_casa.gif', '.metadata', 'logo_casa.gif.metadata',\n        '.properties', 'logo_casa.gif.properties', '.objects'}\n\n    mover_documento(origem, destino)\n    casa = get_casa_legislativa()\n    casa.logotipo = destino\n    casa.save()\n\n\ndef get_extensao(caminho):\n    mime = magic.from_file(caminho, mime=True)\n    try:\n        return EXTENSOES[mime]\n    except KeyError as e:\n        raise Exception('\\n'.join([\n            'Extens\u00e3o n\u00e3o conhecida para o arquivo:',\n            caminho,\n            'E mimetype:',\n            mime,\n            ' Algumas possibilidades s\u00e3o:', ] +\n            [\"    '{}': '{}',\".format(mime, ext)\n             for ext in mimetypes.guess_all_extensions(mime)] +\n            ['Atualize o c\u00f3digo do dicion\u00e1rio EXTENSOES!']\n        )) from e\n\n\ndef migrar_docs_por_ids(tipo):\n    for campo, base_origem, base_destino in DOCS[tipo]:\n        print('#### Migrando {} de {} ####'.format(campo, tipo.__name__))\n\n        dir_origem, nome_origem = os.path.split(em_media(base_origem))\n        pat = re.compile('^{}$'.format(nome_origem.format('(\\d+)')))\n\n        if not os.path.isdir(dir_origem):\n            print('  >>> O diret\u00f3rio {} n\u00e3o existe! Abortado.'.format(\n                dir_origem))\n            continue\n\n        for arq in os.listdir(dir_origem):\n            match = pat.match(arq)\n            if match:\n                # associa documento ao objeto\n                try:\n                    origem = os.path.join(dir_origem, match.group(0))\n                    id = match.group(1)\n                    obj = tipo.objects.get(pk=id)\n\n                    extensao = get_extensao(origem)\n                    if hasattr(obj, \"ano\"):\n                        destino = base_destino.format(id, extensao, obj.ano)\n                    elif isinstance(obj, DocumentoAcessorio):\n                        destino = base_destino.format(\n                            id, extensao, obj.materia.ano)\n                    else:\n                        destino = base_destino.format(id, extensao)\n                    mover_documento(origem, destino)\n\n                    setattr(obj, campo, destino)\n                    obj.save()\n                except tipo.DoesNotExist:\n                    msg = '  {} (pk={}) n\u00e3o encontrado para documento em [{}]'\n                    print(msg.format(\n                        tipo.__name__, id, destino))\n\n\ndef desconecta_sinais_indexacao():\n    post_save.disconnect(save_texto, NormaJuridica)\n    post_save.disconnect(save_texto, DocumentoAcessorio)\n    post_save.disconnect(save_texto, MateriaLegislativa)\n    post_delete.disconnect(delete_texto, NormaJuridica)\n    post_delete.disconnect(delete_texto, DocumentoAcessorio)\n    post_delete.disconnect(delete_texto, MateriaLegislativa)\n\n\ndef conecta_sinais_indexacao():\n    post_save.connect(save_texto, NormaJuridica)\n    post_save.connect(save_texto, DocumentoAcessorio)\n    post_save.connect(save_texto, MateriaLegislativa)\n    post_delete.connect(delete_texto, NormaJuridica)\n    post_delete.connect(delete_texto, DocumentoAcessorio)\n    post_delete.connect(delete_texto, MateriaLegislativa)\n\n\ndef migrar_documentos():\n    # precisamos excluir os sinais de post_save e post_delete para n\u00e3o que o\n    # computador n\u00e3o trave com a cria\u00e7\u00e3o de threads desnecess\u00e1rias\n    desconecta_sinais_indexacao()\n\n    # aqui supomos que uma pasta chamada sapl_documentos est\u00e1 em MEDIA_ROOT\n    # com o conte\u00fado da pasta de mesmo nome do zope\n    # Os arquivos da pasta ser\u00e3o movidos para a nova estrutura e a pasta ser\u00e1\n    # apagada\n    migrar_docs_logo()\n    for tipo in [\n        Parlamentar,\n        MateriaLegislativa,\n        DocumentoAcessorio,\n        NormaJuridica,\n        SessaoPlenaria,\n        Proposicao,\n        DocumentoAdministrativo,\n      \tDocumentoAcessorioAdministrativo,\n    ]:\n        migrar_docs_por_ids(tipo)\n\n    sobrando = [os.path.join(dir, file)\n                for (dir, _, files) in os.walk(em_media('sapl_documentos'))\n                for file in files]\n    if sobrando:\n        print('\\n#### Encerrado ####\\n\\n'\n              '{} documentos sobraram sem ser migrados!!!'.format(\n                  len(sobrando)))\n        for doc in sobrando:\n            print('  {}'. format(doc))\n            #\n    # reconex\u00e3o dos sinais desligados no inicio da migra\u00e7\u00e3o de documentos\n    conecta_sinais_indexacao()\n", "path": "sapl/legacy/migracao_documentos.py"}], "after_files": [{"content": "import mimetypes\nimport os\nimport re\n\nimport magic\nfrom django.db.models.signals import post_delete, post_save\n\nfrom sapl.base.models import CasaLegislativa\nfrom sapl.materia.models import (DocumentoAcessorio, MateriaLegislativa,\n                                 Proposicao)\nfrom sapl.norma.models import NormaJuridica\nfrom sapl.parlamentares.models import Parlamentar\nfrom sapl.protocoloadm.models import (DocumentoAcessorioAdministrativo,\n                                      DocumentoAdministrativo)\nfrom sapl.sessao.models import SessaoPlenaria\nfrom sapl.settings import MEDIA_ROOT\n\n# MIGRA\u00c7\u00c3O DE DOCUMENTOS  ###################################################\nEXTENSOES = {\n    'application/msword': '.doc',\n    'application/pdf': '.pdf',\n    'application/vnd.oasis.opendocument.text': '.odt',\n    'application/vnd.openxmlformats-officedocument.wordprocessingml.document': '.docx',  # noqa\n    'application/xml': '.xml',\n    'application/zip': '.zip',\n    'image/jpeg': '.jpeg',\n    'image/png': '.png',\n    'text/html': '.html',\n    'text/rtf': '.rtf',\n    'text/x-python': '.py',\n    'text/plain': '.ksh',\n    'text/plain': '.c',\n    'text/plain': '.h',\n    'text/plain': '.txt',\n    'text/plain': '.bat',\n    'text/plain': '.pl',\n    'text/plain': '.asc',\n    'text/plain': '.text',\n    'text/plain': '.pot',\n    'text/plain': '.brf',\n    'text/plain': '.srt',\n\n    # sem extensao\n    'application/octet-stream': '',  # bin\u00e1rio\n    'inode/x-empty': '',  # vazio\n}\n\nDOCS = {\n    CasaLegislativa: [(\n        'logotipo',\n        'props_sapl/logo_casa.gif',\n        'casa/logotipo/logo_casa.gif')],\n    Parlamentar: [(\n        'fotografia',\n        'parlamentar/fotos/{}_foto_parlamentar',\n        'public/parlamentar/{0}/{0}_foto_parlamentar{1}')],\n    MateriaLegislativa: [(\n        'texto_original',\n        'materia/{}_texto_integral',\n        'public/materialegislativa/{2}/{0}/{0}_texto_integral{1}')],\n    DocumentoAcessorio: [(\n        'arquivo',\n        'materia/{}',\n        'public/documentoacessorio/{2}/{0}/{0}{1}')],\n    NormaJuridica: [(\n        'texto_integral',\n        'norma_juridica/{}_texto_integral',\n        'public/normajuridica/{2}/{0}/{0}_texto_integral{1}')],\n    SessaoPlenaria: [\n        ('upload_ata',\n         'ata_sessao/{}_ata_sessao',\n         'public/sessaoplenaria/{0}/ata/{0}_ata_sessao{1}'),\n        ('upload_anexo',\n         'anexo_sessao/{}_texto_anexado',\n         'public/sessaoplenaria/{0}/anexo/{0}_texto_anexado{1}')\n    ],\n    Proposicao: [(\n        'texto_original',\n        'proposicao/{}',\n        'private/proposicao/{0}/{0}{1}')],\n    DocumentoAdministrativo: [(\n        'texto_integral',\n        'administrativo/{}_texto_integral',\n        'private/documentoadministrativo/{0}/{0}_texto_integral{1}')\n    ],\n    DocumentoAcessorioAdministrativo: [(\n        'arquivo',\n        'administrativo/{}',\n        'private/documentoacessorioadministrativo/{0}/{0}_acessorio_administrativo{1}')\n    ],\n}\n\nDOCS = {tipo: [(campo,\n                os.path.join('sapl_documentos', origem),\n                os.path.join('sapl', destino))\n               for campo, origem, destino in campos]\n        for tipo, campos in DOCS.items()}\n\n\ndef em_media(caminho):\n    return os.path.join(MEDIA_ROOT, caminho)\n\n\ndef mover_documento(origem, destino):\n    origem, destino = [em_media(c) if not os.path.isabs(c) else c\n                       for c in (origem, destino)]\n    os.makedirs(os.path.dirname(destino), exist_ok=True)\n    os.rename(origem, destino)\n\n\ndef get_casa_legislativa():\n    casa = CasaLegislativa.objects.first()\n    if not casa:\n        casa = CasaLegislativa.objects.create(**{k: 'PREENCHER...' for k in [\n            'codigo', 'nome', 'sigla', 'endereco', 'cep', 'municipio', 'uf',\n        ]})\n    return casa\n\n\ndef migrar_docs_logo():\n    print('#### Migrando logotipo da casa ####')\n    [(_, origem, destino)] = DOCS[CasaLegislativa]\n    props_sapl = os.path.dirname(origem)\n\n    # a pasta props_sapl deve conter apenas o origem e metadatas!\n    # Edit: Aparentemente h\u00e1 diret\u00f3rio que cont\u00e9m properties ao inv\u00e9s de\n    # metadata. O assert foi modificado para essa situa\u00e7\u00e3o.\n    assert set(os.listdir(em_media(props_sapl))) < {\n        'logo_casa.gif', '.metadata', 'logo_casa.gif.metadata',\n        '.properties', 'logo_casa.gif.properties', '.objects'}\n\n    mover_documento(origem, destino)\n    casa = get_casa_legislativa()\n    casa.logotipo = destino\n    casa.save()\n\n\ndef get_extensao(caminho):\n    mime = magic.from_file(caminho, mime=True)\n    try:\n        return EXTENSOES[mime]\n    except KeyError as e:\n        raise Exception('\\n'.join([\n            'Extens\u00e3o n\u00e3o conhecida para o arquivo:',\n            caminho,\n            'E mimetype:',\n            mime,\n            ' Algumas possibilidades s\u00e3o:', ] +\n            [\"    '{}': '{}',\".format(mime, ext)\n             for ext in mimetypes.guess_all_extensions(mime)] +\n            ['Atualize o c\u00f3digo do dicion\u00e1rio EXTENSOES!']\n        )) from e\n\n\ndef migrar_docs_por_ids(tipo):\n    for campo, base_origem, base_destino in DOCS[tipo]:\n        print('#### Migrando {} de {} ####'.format(campo, tipo.__name__))\n\n        dir_origem, nome_origem = os.path.split(em_media(base_origem))\n        pat = re.compile('^{}$'.format(nome_origem.format('(\\d+)')))\n\n        if not os.path.isdir(dir_origem):\n            print('  >>> O diret\u00f3rio {} n\u00e3o existe! Abortado.'.format(\n                dir_origem))\n            continue\n\n        for arq in os.listdir(dir_origem):\n            match = pat.match(arq)\n            if match:\n                # associa documento ao objeto\n                try:\n                    origem = os.path.join(dir_origem, match.group(0))\n                    id = match.group(1)\n                    obj = tipo.objects.get(pk=id)\n\n                    extensao = get_extensao(origem)\n                    if hasattr(obj, \"ano\"):\n                        destino = base_destino.format(id, extensao, obj.ano)\n                    elif isinstance(obj, DocumentoAcessorio):\n                        destino = base_destino.format(\n                            id, extensao, obj.materia.ano)\n                    else:\n                        destino = base_destino.format(id, extensao)\n                    mover_documento(origem, destino)\n\n                    setattr(obj, campo, destino)\n                    obj.save()\n                except tipo.DoesNotExist:\n                    msg = '  {} (pk={}) n\u00e3o encontrado para documento em [{}]'\n                    print(msg.format(\n                        tipo.__name__, id, destino))\n\n\ndef migrar_documentos():\n    # aqui supomos que uma pasta chamada sapl_documentos est\u00e1 em MEDIA_ROOT\n    # com o conte\u00fado da pasta de mesmo nome do zope\n    # Os arquivos da pasta ser\u00e3o movidos para a nova estrutura e a pasta ser\u00e1\n    # apagada\n    migrar_docs_logo()\n    for tipo in [\n        Parlamentar,\n        MateriaLegislativa,\n        DocumentoAcessorio,\n        NormaJuridica,\n        SessaoPlenaria,\n        Proposicao,\n        DocumentoAdministrativo,\n      \tDocumentoAcessorioAdministrativo,\n    ]:\n        migrar_docs_por_ids(tipo)\n\n    sobrando = [os.path.join(dir, file)\n                for (dir, _, files) in os.walk(em_media('sapl_documentos'))\n                for file in files]\n    if sobrando:\n        print('\\n#### Encerrado ####\\n\\n'\n              '{} documentos sobraram sem ser migrados!!!'.format(\n                  len(sobrando)))\n        for doc in sobrando:\n            print('  {}'. format(doc))\n", "path": "sapl/legacy/migracao_documentos.py"}]}