Skip to content

Commit

Permalink
Us 44630 relazione persona uo (#202)
Browse files Browse the repository at this point in the history
* wip: initial
* wip: check_persone
* wip: working on check_persone
* feat: check_persone export, hide organizzazione_riferimento, overwrite with new spec in serializer
* fix: multiple connections now rendered in new row each
* chore: trying to fix different ci and precommit configs with black and flake 8
* chore: remove now useless tests for ct_persona
  • Loading branch information
deodorhunter authored Aug 11, 2023
1 parent 9272ff6 commit aa528ed
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 50 deletions.
211 changes: 211 additions & 0 deletions src/design/plone/contenttypes/browser/manage_content/check_persone.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
from DateTime import DateTime
from openpyxl import Workbook
from openpyxl.styles import Alignment
from openpyxl.styles import Font
from openpyxl.styles import PatternFill
from openpyxl.utils import get_column_letter
from plone import api
from Products.Five import BrowserView
from zope.globalrequest import getRequest
from zope.component import getMultiAdapter
from plone.restapi.interfaces import ISerializeToJsonSummary

import io


class CheckPersone(BrowserView):
cds = None

def is_anonymous(self):
return api.user.is_anonymous()

def get_relations(self, obj, field):
return api.relation.get(source=obj, relationship=field, unrestricted=False)

def get_related_objects(self, obj, field):
""" """
items = []
relations = self.get_relations(obj, field)

for rel in relations:
rel_obj = rel.to_object
if rel_obj is not None:
summary = getMultiAdapter(
(rel_obj, getRequest()), ISerializeToJsonSummary
)()
items.append(summary)
return sorted(items, key=lambda k: k["title"])

def information_dict(self, persona):
relations = self.get_related_objects(persona, "organizzazione_riferimento")
return {
"title": getattr(persona, "title"),
"has_related_uo": bool(relations),
"organizzazione_riferimento": relations,
}

def plone2volto(self, url):
portal_url = api.portal.get().absolute_url()
frontend_domain = api.portal.get_registry_record(
"volto.frontend_domain", default=""
)
if frontend_domain and url.startswith(portal_url):
return url.replace(portal_url, frontend_domain, 1)
return url

def get_persone(self):
if self.is_anonymous():
return []
pc = api.portal.get_tool("portal_catalog")

# show_inactive ha sempre avuto una gestione... particolare! aggiungo ai
# kw effectiveRange = DateTime() che è quello che fa Products.CMFPlone
# nel CatalogTool.py
query = {
"portal_type": "Persona",
# "review_state": "published",
}
brains = pc(query, **{"effectiveRange": DateTime()})
results = {}
for brain in brains:
persona = brain.getObject()

information_dict = self.information_dict(persona)

if not information_dict.get("has_related_uo"):
continue

parent = persona.aq_inner.aq_parent
if parent.title not in results:
results[parent.title] = {
"url": self.plone2volto(parent.absolute_url()),
"children": [],
}

results[parent.title]["children"].append(
{
"title": persona.title,
"url": self.plone2volto(persona.absolute_url()),
"data": {
"title": information_dict.get("title"),
"has_related_uo": information_dict.get("has_related_uo", "V"),
"organizzazione_riferimento": information_dict.get(
"organizzazione_riferimento"
),
},
}
)

results = dict(sorted(results.items()))
for key in results:
results[key]["children"].sort(key=lambda x: x["title"])
return results


class DownloadCheckPersone(CheckPersone):
CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"

def __call__(self):
HEADER = [
"Titolo",
"Ha organizzazioni di riferimento",
"Unità org. di riferimento",
]

EMPTY_ROW = [""] * 3

persone = self.get_persone()

workbook = Workbook()
sheet = workbook.active
sheet.title = "Check Persone"
header_font = Font(bold=True)
section_link_font = Font(underline="single", color="0563C1", size=14)
link_fill = PatternFill(fill_type="solid", fgColor="DDDDDD")
link_font = Font(underline="single", color="0563C1")
section_fill = PatternFill(fill_type="solid", fgColor="E9E9E9")
alignment = Alignment(horizontal="center", vertical="top", wrapText=True)

section_row_height = int(14 * 1.5)

for category, category_data in persone.items():
section_url = category_data["url"]
section_title = category
section_row = [section_title, "", ""]
sheet.append(section_row)
section_cell = sheet.cell(row=sheet.max_row, column=1)

section_cell.alignment = alignment
section_cell.hyperlink = section_url
sheet.merge_cells(
start_row=sheet.max_row,
start_column=1,
end_row=sheet.max_row,
end_column=3,
) # noqa
for row in sheet.iter_rows(
min_row=sheet.max_row, max_row=sheet.max_row, min_col=1, max_col=3
): # noqa
sheet.row_dimensions[row[0].row].height = section_row_height
for cell in row:
cell.fill = section_fill
cell.font = section_link_font

sheet.append(HEADER)
for col in range(1, len(HEADER) + 1):
header_cell = sheet.cell(row=sheet.max_row, column=col)
header_cell.fill = link_fill
header_cell.font = header_font

for col in sheet.columns:
column_letter = get_column_letter(col[0].column)
sheet.column_dimensions[column_letter].width = 35

for persona in category_data["children"]:
organizzazioni = "\n".join(
[
f"{x.get('title')} - {self.plone2volto(x.get('@id'))}"
for x in persona["data"]["organizzazione_riferimento"]
]
) # noqa
title_url = persona["url"]
dati_persona = [
persona["title"],
"X" if persona["data"]["has_related_uo"] else "",
organizzazioni,
]
row = dati_persona
sheet.append(row)

title_cell = sheet.cell(row=sheet.max_row, column=1)
check_cell = sheet.cell(row=sheet.max_row, column=2)
check_cell.alignment = check_cell.alignment.copy(horizontal="center")
title_cell.hyperlink = title_url
title_cell.font = link_font
column_letter_unit = get_column_letter(title_cell.column)
sheet.column_dimensions[column_letter_unit].width = 60
max_index = sheet.max_row
for org in organizzazioni.split("\n"):
org_title, org_url = org.split(" - ")
org_link = f'=HYPERLINK("{org_url}", "{org_title}")'
org_cell = sheet.cell(row=max_index, column=3)
org_cell.value = org_link
org_cell.font = link_font
org_cell.alignment = org_cell.alignment.copy(horizontal="left")
column_letter_unit = get_column_letter(org_cell.column)
sheet.column_dimensions[column_letter_unit].width = 80
max_index += 1

sheet.append(EMPTY_ROW)
sheet.append(EMPTY_ROW)

bytes_io = io.BytesIO()
workbook.save(bytes_io)
data = bytes_io.getvalue()
self.request.response.setHeader("Content-Length", len(data))
self.request.RESPONSE.setHeader("Content-Type", self.CT)
self.request.response.setHeader(
"Content-Disposition",
"attachment; filename=check_persone.xlsx",
)
return data
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,11 @@
class=".check_servizi.DownloadCheckServizi"
permission="zope2.Public"
/>
<browser:page
name="download_check_persone"
for="*"
class=".check_persone.DownloadCheckPersone"
permission="zope2.Public"
/>

</configure>
5 changes: 5 additions & 0 deletions src/design/plone/contenttypes/interfaces/persona.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ class IPersona(model.Schema, IDesignPloneContentType):
"La dimensione suggerita è 100x180px.",
),
)
# Questo campo per direttive e richieste viene nascosto nella form
# Lo si tiene perche si vuole evitare di perder dati tra le migrazioni
# e magari non poter piu' usare la feature collegata, ossia
# la check persone, in quanto relazioni potrebbero rompersi o perdersi
organizzazione_riferimento = RelationList(
title=_(
"organizzazione_riferimento_label",
Expand All @@ -50,6 +54,7 @@ class IPersona(model.Schema, IDesignPloneContentType):
default=[],
required=False,
)
form.omitted("organizzazione_riferimento")
incarichi_persona = RelationList(
title=_(
"incarichi_label",
Expand Down
12 changes: 7 additions & 5 deletions src/design/plone/contenttypes/restapi/serializers/persona.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@ def __call__(self, version=None, include_items=True):
responsabile_di = self.related_contents(field="responsabile")
assessore_di = self.related_contents(field="assessore_riferimento")

if strutture_correlate:
result["strutture_correlate"] = strutture_correlate
if responsabile_di:
result["responsabile_di"] = responsabile_di
organizzazioni = []
if assessore_di:
result["assessore_di"] = assessore_di
organizzazioni = organizzazioni + assessore_di
if responsabile_di:
organizzazioni = organizzazioni + responsabile_di
if strutture_correlate:
organizzazioni = organizzazioni + strutture_correlate
result["organizzazione_riferimento"] = organizzazioni
return result
45 changes: 0 additions & 45 deletions src/design/plone/contenttypes/tests/test_ct_persona.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,51 +85,6 @@ def setUp(self):
def tearDown(self):
self.api_session.close()

def test_persona_strutture_correlate(self):
uo = api.content.create(
container=self.portal,
type="UnitaOrganizzativa",
title="UO 1",
persone_struttura=[self.persona_ref],
)
commit()
response = self.api_session.get(self.persona.absolute_url())
res = response.json()

self.assertIn("strutture_correlate", list(res.keys()))
self.assertEqual(len(res["strutture_correlate"]), 1)
self.assertEqual(res["strutture_correlate"][0]["title"], uo.title)

def test_persona_responsabile_di(self):
uo = api.content.create(
container=self.portal,
type="UnitaOrganizzativa",
title="UO 1",
responsabile=[self.persona_ref],
)
commit()
response = self.api_session.get(self.persona.absolute_url())
res = response.json()

self.assertIn("responsabile_di", list(res.keys()))
self.assertEqual(len(res["responsabile_di"]), 1)
self.assertEqual(res["responsabile_di"][0]["title"], uo.title)

def test_persona_assessore_di(self):
uo = api.content.create(
container=self.portal,
type="UnitaOrganizzativa",
title="UO 1",
assessore_riferimento=[self.persona_ref],
)
commit()
response = self.api_session.get(self.persona.absolute_url())
res = response.json()

self.assertIn("assessore_di", list(res.keys()))
self.assertEqual(len(res["assessore_di"]), 1)
self.assertEqual(res["assessore_di"][0]["title"], uo.title)

def test_atto_di_nomina_incarico(self):
incarico = api.content.create(
container=self.persona.incarichi, type="Incarico", title="Sindaco"
Expand Down
1 change: 1 addition & 0 deletions src/design/plone/contenttypes/upgrades/to_7001.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ def create_incarico_for_persona(context):
)
# incarico.persona = [RelationValue(intids.getId(persona))]
api.relation.create(source=incarico, target=persona, relationship="persona")

if safe_hasattr(persona, "organizzazione_riferimento"):
incarico.unita_organizzativa = persona.organizzazione_riferimento

Expand Down

0 comments on commit aa528ed

Please sign in to comment.