We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Migração de Documentos: separar documentos por ano
Nas models de MateriaLegislativa, DocumentoAcessorio e NormaJurídica, seria bom alterar a estrutura de pastas para organizar os documentos migrados de acordo com o ano que foi criado.

Como exemplo, a mudança passaria de:
`media/sapl/{model_name}/{id}/{id}_nome_documento.pdf`
Para:
`media/sapl/{model_name}/{ano}/{id}/{id}_nome_documento.pdf`

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

{"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@@ -55,40 +55,40 @@\n     Parlamentar: [(\n         'fotografia',\n         'parlamentar/fotos/{}_foto_parlamentar',\n-        'parlamentar/{0}/{0}_foto_parlamentar{1}')],\n+        'public/parlamentar/{0}/{0}_foto_parlamentar{1}')],\n     MateriaLegislativa: [(\n         'texto_original',\n         'materia/{}_texto_integral',\n-        'materialegislativa/{0}/{0}_texto_integral{1}')],\n+        'public/materialegislativa/{2}/{0}/{0}_texto_integral{1}')],\n     DocumentoAcessorio: [(\n         'arquivo',\n         'materia/{}',\n-        'documentoacessorio/{0}/{0}{1}')],\n+        'public/documentoacessorio/{2}/{0}/{0}{1}')],\n     NormaJuridica: [(\n         'texto_integral',\n         'norma_juridica/{}_texto_integral',\n-        'normajuridica/{0}/{0}_texto_integral{1}')],\n+        'public/normajuridica/{2}/{0}/{0}_texto_integral{1}')],\n     SessaoPlenaria: [\n         ('upload_ata',\n          'ata_sessao/{}_ata_sessao',\n-         'sessaoplenaria/{0}/ata/{0}_ata_sessao{1}'),\n+         'public/sessaoplenaria/{0}/ata/{0}_ata_sessao{1}'),\n         ('upload_anexo',\n          'anexo_sessao/{}_texto_anexado',\n-         'sessaoplenaria/{0}/anexo/{0}_texto_anexado{1}')\n+         'public/sessaoplenaria/{0}/anexo/{0}_texto_anexado{1}')\n     ],\n     Proposicao: [(\n         'texto_original',\n         'proposicao/{}',\n-        'proposicao/{0}/{0}{1}')],\n+        'private/proposicao/{0}/{0}{1}')],\n     DocumentoAdministrativo: [(\n         'texto_integral',\n         'administrativo/{}_texto_integral',\n-        'documentoadministrativo/{0}/{0}_texto_integral{1}')       \n+        'private/documentoadministrativo/{0}/{0}_texto_integral{1}')\n     ],\n     DocumentoAcessorioAdministrativo: [(\n-    \t 'arquivo',\n+        'arquivo',\n         'administrativo/{}',\n-        'documentoacessorioadministrativo/{0}/{0}_acessorio_administrativo{1}')\n+        'private/documentoacessorioadministrativo/{0}/{0}_acessorio_administrativo{1}')\n     ],\n }\n \n@@ -169,15 +169,22 @@\n         for arq in os.listdir(dir_origem):\n             match = pat.match(arq)\n             if match:\n-                origem = os.path.join(dir_origem, match.group(0))\n-                id = match.group(1)\n-                extensao = get_extensao(origem)\n-                destino = base_destino.format(id, extensao)\n-                mover_documento(origem, destino)\n-\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", "issue": "Migra\u00e7\u00e3o de Documentos: separar documentos por ano\nNas models de MateriaLegislativa, DocumentoAcessorio e NormaJur\u00eddica, seria bom alterar a estrutura de pastas para organizar os documentos migrados de acordo com o ano que foi criado.\r\n\r\nComo exemplo, a mudan\u00e7a passaria de:\r\n`media/sapl/{model_name}/{id}/{id}_nome_documento.pdf`\r\nPara:\r\n`media/sapl/{model_name}/{ano}/{id}/{id}_nome_documento.pdf`\n", "before_files": [{"content": "import mimetypes\nimport os\nimport re\n\nimport magic\n\nfrom django.db.models.signals import post_delete, post_save\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 DocumentoAdministrativo\nfrom sapl.protocoloadm.models import DocumentoAcessorioAdministrativo\nfrom sapl.sessao.models import SessaoPlenaria\nfrom sapl.settings import MEDIA_ROOT\nfrom sapl.utils import delete_texto, save_texto\n\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        'parlamentar/{0}/{0}_foto_parlamentar{1}')],\n    MateriaLegislativa: [(\n        'texto_original',\n        'materia/{}_texto_integral',\n        'materialegislativa/{0}/{0}_texto_integral{1}')],\n    DocumentoAcessorio: [(\n        'arquivo',\n        'materia/{}',\n        'documentoacessorio/{0}/{0}{1}')],\n    NormaJuridica: [(\n        'texto_integral',\n        'norma_juridica/{}_texto_integral',\n        'normajuridica/{0}/{0}_texto_integral{1}')],\n    SessaoPlenaria: [\n        ('upload_ata',\n         'ata_sessao/{}_ata_sessao',\n         'sessaoplenaria/{0}/ata/{0}_ata_sessao{1}'),\n        ('upload_anexo',\n         'anexo_sessao/{}_texto_anexado',\n         'sessaoplenaria/{0}/anexo/{0}_texto_anexado{1}')\n    ],\n    Proposicao: [(\n        'texto_original',\n        'proposicao/{}',\n        'proposicao/{0}/{0}{1}')],\n    DocumentoAdministrativo: [(\n        'texto_integral',\n        'administrativo/{}_texto_integral',\n        'documentoadministrativo/{0}/{0}_texto_integral{1}')       \n    ],\n    DocumentoAcessorioAdministrativo: [(\n    \t 'arquivo',\n        'administrativo/{}',\n        '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                origem = os.path.join(dir_origem, match.group(0))\n                id = match.group(1)\n                extensao = get_extensao(origem)\n                destino = base_destino.format(id, extensao)\n                mover_documento(origem, destino)\n\n                # associa documento ao objeto\n                try:\n                    obj = tipo.objects.get(pk=id)\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\n\nfrom django.db.models.signals import post_delete, post_save\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 DocumentoAdministrativo\nfrom sapl.protocoloadm.models import DocumentoAcessorioAdministrativo\nfrom sapl.sessao.models import SessaoPlenaria\nfrom sapl.settings import MEDIA_ROOT\nfrom sapl.utils import delete_texto, save_texto\n\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"}]}