work in progress

my-importa-contatti-google
Guido Longoni 2023-08-29 13:39:47 +02:00
parent 6e8764108d
commit 25ff9fe0eb
9 changed files with 402 additions and 23 deletions

View File

@ -12,8 +12,9 @@ from django.contrib import admin
from django.core.cache import cache
from django.core.paginator import Paginator
from django.db.models import F
from . import models
from django.template.response import TemplateResponse
from django.urls import path
from . import models, views
# Modified version of a GIST I found in a SO thread
# cfr. http://masnun.rocks/2017/03/20/django-admin-expensive-count-all-queries/
@ -71,11 +72,43 @@ class CachingPaginator(Paginator):
# def has_delete_permission(self, request, obj=None):
# return False
class ImportaDaGoogleMixin():
change_list_template = 'admin/contatti_app/import_google_contacts_button.html'
def get_model_info(self):
app_label = self.model._meta.app_label
return (app_label, self.model._meta.model_name)
def get_urls(self):
info = self.get_model_info()
urls = [path('googleimport/',
self.admin_site.admin_view(self.googleimport),
name='%s_%s_googleimport' % info),
path('googleimport/confirm',
self.admin_site.admin_view(self.googleimport_confirm),
name='%s_%s_googleimport_confirm' % info),]
urls += super().get_urls()
return urls
def googleimport(self, request):
context = dict( self.admin_site.each_context(request), )
if request.method == 'POST' and request.FILES.get('csv_file'):
app_label, model_name = self.get_model_info()
context.update({'app_label':app_label, 'model_name':model_name})
context.update(views.googleimport_preview(request))
return TemplateResponse(request, 'admin/contatti_app/import_google_contacts_preview.html', context)
else:
return TemplateResponse(request, "admin/contatti_app/import_google_contacts.html", context)
def googleimport_confirm(self, request):
print('QUIIIIII')
pass
# --------------- FINE PREFISSO TEMPLATE ---------------
@admin.register(models.ContattoAziendale)
class ContattoAziendaleAdmin(ImportExportModelAdmin, AutocompleteAdmin):
class ContattoAziendaleAdmin(HiddenModel, ImportExportModelAdmin, AutocompleteAdmin):
# resource = resources.ContattoAziendaleResource
# list_per_page = 15
# paginator = CachingPaginator
@ -84,8 +117,9 @@ class ContattoAziendaleAdmin(ImportExportModelAdmin, AutocompleteAdmin):
list_display = ('persona','azienda','is_personale')
pass
@admin.register(models.Recapito)
class RecapitoAdmin(ImportExportModelAdmin, PolymorphicParentModelAdmin):
class RecapitoAdmin(ImportaDaGoogleMixin, ImportExportModelAdmin, PolymorphicParentModelAdmin):
# resource = resources.RecapitoResource
# list_per_page = 15
# paginator = CachingPaginator
@ -201,6 +235,11 @@ class RecapitoInline(StackedPolymorphicInline, DrillDownAutocompleteAdmin):
FaxInline,
)
class AziendaInline(admin.TabularInline):
verbose_name_plural = 'Rapporti con aziende'
model = models.ContattoAziendale
extra = 0
autocomplete_fields = ('azienda',)
@admin.register(models.PersonaFisica)
class PersonaFisicaAdmin(PolymorphicInlineSupportMixin, PolymorphicChildModelAdmin, StackedInlineCollassati,AutocompleteAdmin):
@ -211,7 +250,7 @@ class PersonaFisicaAdmin(PolymorphicInlineSupportMixin, PolymorphicChildModelAdm
show_in_index = False
get_model_perms = lambda self, req: {}
search_fields = ('nome','cognome',)
inlines = (RecapitoInline,)
inlines = (RecapitoInline, AziendaInline)
@admin.register(models.PersonaGiuridica)
@ -282,7 +321,6 @@ class SoggettoContattabileAdmin(PolymorphicParentModelAdmin):
def get_queryset(self, request):
qs=super().get_queryset(request).prefetch_related('polymorphic_ctype')
return qs
pass
@admin.register(models.Indirizzo)
@ -335,7 +373,7 @@ class PersonaInline(admin.TabularInline):
extra = 0
@admin.register(models.Societa)
class SocietaAdmin(ImportExportModelAdmin, AutocompleteAdmin):
class SocietaAdmin(ImportExportModelAdmin, AutocompleteAdmin, StackedInlineCollassati):
# resource = resources.SocietaResource
# list_per_page = 15
# paginator = CachingPaginator

View File

@ -0,0 +1,13 @@
.tabulator-row.tabulator-group.tabulator-group-level-0 {
display: flex;
}
.contatto_group {
display: flex;
flex-grow: 1;
justify-content: flex-end;
}
.contatto_group input[type="checkbox"] {
margin-left: 10px;
}

View File

@ -0,0 +1 @@
/home/guido/2_external_repo/tabulator/dist/css

View File

@ -0,0 +1,92 @@
/* eslint-env browser */
var svg_new = '<svg height="15px" viewBox="0 0 20.865358 11.079615"><path d="m 7.2636719,1.0957031 c -1.2165412,0 -2.3194242,0.2055385 -3.1445313,0.5546875 C 3.7065871,1.8249654 3.3626,2.0337686 3.1074219,2.2890625 2.8522435,2.5443564 2.6835937,2.8594223 2.6835938,3.2050781 h -0.00195 V 7.3886719 C 2.68109,7.7345291 2.850377,8.049159 3.1054688,8.304688 3.3605603,8.560216 3.7045123,8.770598 4.1171875,8.945313 4.9425379,9.294741 6.0464067,9.500087 7.2636719,9.5 8.4809371,9.500087 9.5848064,9.294741 10.410156,8.945313 10.822832,8.770598 11.166783,8.560216 11.421875,8.304688 11.676967,8.049159 11.846254,7.7345292 11.845703,7.3886719 V 3.2050781 h -0.002 c 0,-0.3456558 -0.168649,-0.6607217 -0.423828,-0.9160156 C 11.164743,2.0337686 10.820757,1.8249654 10.408203,1.6503906 9.5830965,1.3012416 8.4802131,1.0957031 7.2636719,1.0957031 Z m 0,0.5976563 c 1.1481408,0 2.185616,0.2012187 2.9101561,0.5078125 0.362271,0.1532968 0.646072,0.333491 0.824219,0.5117187 0.178147,0.1782277 0.248047,0.3375285 0.248047,0.4921875 0,0.1546592 -0.0699,0.3159128 -0.248047,0.4941407 -0.107293,0.1073415 -0.253638,0.215897 -0.431641,0.3183593 -0.01347,0.00779 -0.02717,0.015716 -0.04101,0.023437 -0.06409,0.035594 -0.131451,0.069684 -0.203125,0.1035156 -0.01048,0.00496 -0.02061,0.010706 -0.03125,0.015625 -0.03791,0.017471 -0.07735,0.033923 -0.117188,0.050781 C 9.4492879,4.5175313 8.4118127,4.71875 7.2636719,4.71875 6.1155308,4.71875 5.078056,4.5175313 4.3535156,4.2109375 4.3136751,4.1940787 4.2742344,4.1776272 4.2363281,4.1601562 4.2256608,4.1552398 4.2155886,4.1494924 4.2050781,4.1445312 4.1334043,4.1106999 4.0660401,4.0766094 4.0019531,4.0410156 3.9880576,4.0332981 3.9744617,4.025363 3.9609375,4.0175781 3.7829354,3.9151158 3.6365899,3.8065603 3.5292969,3.6992188 c -0.021921,-0.021931 -0.043785,-0.043017 -0.0625,-0.064453 v -0.00195 c -0.018606,-0.021361 -0.035218,-0.041591 -0.050781,-0.0625 v -0.00195 c -0.015467,-0.020847 -0.030385,-0.040112 -0.042969,-0.060547 v -0.00195 c -0.012491,-0.020374 -0.025392,-0.040544 -0.035156,-0.060547 v -0.00195 C 3.3282274,3.4234402 3.3195992,3.4043551 3.3125,3.3847656 v -0.00195 C 3.30549,3.3632816 3.299523,3.3434656 3.294922,3.3242216 v -0.00195 c -0.00452,-0.019183 -0.00751,-0.037708 -0.00977,-0.056641 v -0.00195 c -0.00218,-0.01891 -0.00384,-0.037943 -0.00391,-0.056641 v -0.00195 c 0,-0.154659 0.0699,-0.3139598 0.2480469,-0.4921875 C 3.7074437,2.5346629 3.9912455,2.3544687 4.3535156,2.2011719 5.078056,1.8945781 6.1155308,1.6933594 7.2636719,1.6933594 Z m 7.1132811,1.359375 v 1.6621094 h -1.660156 v 1.203125 h 1.660156 v 1.6621093 h 1.207031 V 5.9179688 h 1.658204 V 4.7148438 H 15.583984 V 3.0527344 Z M 3.28125,4.2792969 c 0.1936153,0.1566713 0.4234437,0.2944005 0.6855469,0.4160156 0.00495,0.0023 0.01065,0.00357 0.015625,0.00586 0.00346,0.00168 0.00628,0.00419 0.00977,0.00586 0.040174,0.019305 0.082379,0.038596 0.125,0.056641 0.045034,0.019066 0.093959,0.036489 0.140625,0.054687 0.013007,0.00507 0.025932,0.010624 0.039063,0.015625 0.095852,0.036526 0.1946683,0.07073 0.296875,0.1035156 0.012341,0.00396 0.024679,0.00782 0.037109,0.011719 0.7472122,0.2346065 1.6519498,0.3692109 2.6328125,0.3691406 0.9808626,7.03e-5 1.8856003,-0.1345341 2.6328125,-0.3691406 0.012441,-0.00391 0.024758,-0.00776 0.037109,-0.011719 0.1022061,-0.032785 0.2010231,-0.066989 0.2968751,-0.1035156 0.01314,-0.00501 0.02605,-0.010551 0.03906,-0.015625 0.04667,-0.018198 0.09559,-0.035621 0.140625,-0.054687 0.04262,-0.018045 0.08483,-0.037336 0.125,-0.056641 0.0086,-0.00412 0.01692,-0.00755 0.02539,-0.011719 0.262103,-0.1216151 0.491931,-0.2593443 0.685547,-0.4160156 v 3.109375 C 11.246341,7.5434286 11.176045,7.704509 10.998048,7.8828135 10.820048,8.061118 10.538136,8.241123 10.175782,8.394532 9.4510762,8.701349 8.412499,8.902426 7.2636719,8.902344 6.1148446,8.902426 5.0762681,8.701349 4.3515625,8.394531 3.9892098,8.241122 3.7072966,8.061117 3.5292969,7.8828125 3.3512972,7.704508 3.2810036,7.5434276 3.28125,7.3886719 Z" /></svg>';
var svg_exists = '<svg height="15px" viewBox="0 0 20.865358 11.079615" > <path d="M 7.09375 1.734375 C 6.0175632 1.734375 5.0425321 1.8511025 4.3164062 2.0488281 C 3.9533434 2.1476909 3.6535765 2.2648943 3.421875 2.4140625 C 3.1901735 2.5632307 2.9941406 2.7691555 2.9941406 3.0488281 L 2.9941406 7.9335938 C 2.9939037 8.2132834 3.1882841 8.4210992 3.4199219 8.5703125 C 3.6515596 8.7195258 3.9533828 8.8347048 4.3164062 8.9335938 C 5.0424532 9.1313714 6.015542 9.2499587 7.0917969 9.25 C 8.1680517 9.2499587 9.1430938 9.1313714 9.8691406 8.9335938 C 10.075479 8.8773863 10.259228 8.8160851 10.423828 8.7460938 C 10.433375 8.7725445 10.439324 8.7931519 10.449219 8.8203125 C 10.698744 9.5186975 10.915179 9.9065136 11.099609 9.9824219 C 11.170127 10.01279 11.400456 10.027344 11.791016 10.027344 C 12.176151 10.027344 12.431878 9.936094 12.556641 9.7539062 C 14.346707 7.142554 16.288977 4.6626461 18.382812 2.3144531 C 18.561819 2.106962 18.650391 1.967335 18.650391 1.8964844 C 18.650391 1.8509375 18.620215 1.8135676 18.560547 1.7832031 C 18.500878 1.7528386 18.38452 1.7382812 18.210938 1.7382812 C 17.478638 1.7382812 16.943738 1.9207815 16.607422 2.2851562 C 15.31098 3.6920477 13.901222 5.5332046 12.376953 7.8105469 C 12.301011 7.9117621 12.228721 7.9628906 12.158203 7.9628906 C 12.065988 7.9628906 11.910337 7.6580725 11.693359 7.0507812 C 11.58487 6.7268926 11.424444 6.5664062 11.212891 6.5664062 C 11.20532 6.5664062 11.197121 6.5681421 11.189453 6.5683594 L 11.189453 3.0996094 C 11.190931 3.0826875 11.191406 3.0663077 11.191406 3.0488281 C 11.191406 2.7691555 10.997327 2.5632307 10.765625 2.4140625 C 10.533923 2.2648943 10.232204 2.1476909 9.8691406 2.0488281 C 9.1430149 1.8511025 8.1699368 1.734375 7.09375 1.734375 z M 7.09375 2.28125 C 8.129632 2.28125 9.0666329 2.3970035 9.7246094 2.5761719 C 10.053598 2.6657558 10.313417 2.7730446 10.46875 2.8730469 C 10.624083 2.9730492 10.642578 3.0409403 10.642578 3.0488281 C 10.642578 3.0547456 10.63128 3.094598 10.560547 3.15625 C 10.537114 3.1765207 10.504851 3.200005 10.466797 3.2246094 C 10.428416 3.249333 10.382499 3.2755441 10.332031 3.3007812 C 10.281359 3.3260578 10.221973 3.3519274 10.160156 3.3769531 C 10.03513 3.4274722 9.889078 3.4766826 9.7246094 3.5214844 C 9.4784165 3.5885481 9.1902787 3.6468209 8.875 3.6933594 C 8.5595149 3.739862 8.2129085 3.7744315 7.8476562 3.7949219 C 7.7262991 3.8017113 7.6000346 3.8070307 7.4746094 3.8105469 C 7.3491488 3.8140457 7.2225675 3.816383 7.09375 3.8164062 L 7.0917969 3.8164062 C 6.9629875 3.8163879 6.8344379 3.814044 6.7089844 3.8105469 C 6.5835928 3.8070485 6.4591801 3.8016946 6.3378906 3.7949219 L 6.3359375 3.7949219 C 6.2140343 3.7880968 6.0955676 3.7794611 5.9785156 3.7695312 C 5.8613528 3.7595994 5.7463832 3.7491617 5.6347656 3.7363281 C 5.5239253 3.7235725 5.4152033 3.7087816 5.3105469 3.6933594 L 5.3085938 3.6933594 C 5.2033566 3.6778305 5.1021799 3.6606012 5.0039062 3.6425781 C 4.807166 3.6065307 4.6254316 3.5662765 4.4609375 3.5214844 C 4.1319493 3.4319002 3.8721294 3.3246117 3.7167969 3.2246094 C 3.6779897 3.1996111 3.6485732 3.1768003 3.625 3.15625 C 3.5542672 3.0945975 3.5429687 3.054744 3.5429688 3.0488281 C 3.5429688 3.0409381 3.5614639 2.973049 3.7167969 2.8730469 C 3.8721294 2.7730446 4.1319493 2.6657561 4.4609375 2.5761719 C 5.1189139 2.3970035 6.057868 2.28125 7.09375 2.28125 z M 3.5429688 3.7558594 C 3.7550439 3.8714103 4.0133573 3.9663072 4.3164062 4.0488281 C 4.407172 4.0735438 4.501354 4.0969725 4.5996094 4.1191406 C 4.6978042 4.1413113 4.799225 4.1621753 4.9042969 4.1816406 L 4.90625 4.1816406 C 5.0114179 4.2011064 5.1189655 4.2197196 5.2304688 4.2363281 C 5.3418556 4.2529344 5.4551258 4.2676565 5.5722656 4.28125 L 5.5742188 4.28125 C 5.6907573 4.2947538 5.8099683 4.3060353 5.9316406 4.3164062 L 5.9335938 4.3164062 C 6.0553161 4.3267625 6.1803386 4.33668 6.3066406 4.34375 L 6.3085938 4.34375 C 6.4349352 4.3508039 6.5629932 4.3557605 6.6933594 4.359375 L 6.6953125 4.359375 C 6.8257437 4.3629886 6.9599014 4.3652163 7.09375 4.3652344 C 7.2276071 4.3652112 7.3617497 4.3629901 7.4921875 4.359375 C 7.622588 4.3557426 7.7524955 4.350821 7.8789062 4.34375 C 8.0052771 4.3366625 8.1320954 4.3267793 8.2539062 4.3164062 C 8.3756661 4.306018 8.4966506 4.2947704 8.6132812 4.28125 C 8.847089 4.2541045 9.0715107 4.2204625 9.28125 4.1816406 C 9.4908528 4.1427951 9.6881839 4.0981213 9.8691406 4.0488281 C 10.171331 3.9665409 10.430385 3.872914 10.642578 3.7578125 L 10.642578 6.7265625 C 10.626828 6.7343252 10.611709 6.7397122 10.595703 6.7480469 C 10.215992 6.9454165 10.025391 7.1507293 10.025391 7.3632812 C 10.025391 7.5032466 10.100335 7.7945087 10.242188 8.2246094 C 10.100396 8.2877744 9.9284599 8.3507205 9.7246094 8.40625 C 9.0667355 8.585457 8.1277445 8.7011321 7.0917969 8.7011719 C 6.0558492 8.7011321 5.1188113 8.585457 4.4609375 8.40625 C 4.1320006 8.3166465 3.8720253 8.209368 3.7167969 8.109375 C 3.5615683 8.009382 3.5429621 7.9414828 3.5429688 7.9335938 L 3.5429688 3.7558594 z " /> </svg>';
var svg_ambig_contact = '<svg height="15px" viewBox="0 0 20.865358 11.079615" > <path d="M 3.8302981,1.0013911 A 2.0503062,2.2045549 0 0 0 1.7788149,3.2052623 2.0503062,2.2045549 0 0 0 3.8302981,5.4091335 2.0503062,2.2045549 0 0 0 5.8788163,3.2052623 2.0503062,2.2045549 0 0 0 3.8302981,1.0013911 Z M 2.0841658,5.4270512 A 3.5779214,7.3094974 0 0 0 0.27577761,10.203897 H 7.2336251 A 3.5779214,7.3094974 0 0 0 5.5023158,5.4987218 2.9130581,3.2758681 0 0 1 3.8362271,6.0971715 2.9130581,3.2758681 0 0 1 2.0841658,5.4270512 Z" /> <path d="M 16.55452,1.0000004 A 2.0503062,2.2045549 0 0 0 14.503037,3.2038716 2.0503062,2.2045549 0 0 0 16.55452,5.4077428 2.0503062,2.2045549 0 0 0 18.603038,3.2038716 2.0503062,2.2045549 0 0 0 16.55452,1.0000004 Z M 14.808388,5.4256605 A 3.5779214,7.3094974 0 0 0 13,10.202506 h 6.957847 A 3.5779214,7.3094974 0 0 0 18.226538,5.4973311 2.9130581,3.2758681 0 0 1 16.560449,6.0957808 2.9130581,3.2758681 0 0 1 14.808388,5.4256605 Z" /> <path d="m 9.273908,8.0236085 h 1.337985 V 9.4498211 H 9.273908 Z M 10.572346,7.1982019 H 9.3134544 V 6.33349 q 0,-0.5671161 0.1845497,-0.9320918 Q 9.682554,5.0364224 10.275749,4.5535314 l 0.593196,-0.499736 q 0.37569,-0.2975956 0.540467,-0.5615011 0.171367,-0.2639056 0.171367,-0.5390412 0,-0.4997359 -0.43501,-0.8085617 -0.428419,-0.3088256 -1.140253,-0.3088256 -0.5206941,0 -1.1138895,0.1965253 Q 8.3050221,2.2289167 7.6656893,2.6051224 V 1.5495002 Q 8.2852489,1.2294447 8.9179908,1.0722244 9.5573236,0.915004 10.236202,0.915004 q 1.212756,0 1.944363,0.544656 0.738199,0.5446562 0.738199,1.4374431 0,0.4267408 -0.237278,0.8141766 -0.237278,0.3818208 -0.830473,0.8647117 l -0.580014,0.482891 q -0.30978,0.2639056 -0.441601,0.4155109 -0.12523,0.1459903 -0.177959,0.2863656 -0.03955,0.1179151 -0.05932,0.2863656 -0.01978,0.1684503 -0.01978,0.460431 z" /> </svg>';
var svg_email = '<svg height="15px" viewBox="0 0 20.865358 11.079615"><path d="m 5.921879,0.01450083 c -0.35223,-0.01029 -0.62994,0.200795 -0.77148,0.4375 -0.14154,0.236705 -0.19544,0.49987 -0.23438,0.71093697 L 2.667979,10.17661 a 0.27260457,0.27260457 0 0 0 -0.002,0.0078 c -0.0381,0.17065 -0.0208,0.34282 0.0527,0.482422 0.0735,0.139602 0.19102,0.235998 0.3125,0.296875 0.24296,0.121753 0.50975,0.134614 0.74219,0.09766 l -0.043,0.0039 c 4.65777,-0.0059 9.31901,0.01077 13.97851,-0.0098 a 0.27260457,0.27260457 0 0 0 0.0469,-0.0059 c 0.39241,-0.07261 0.62108,-0.382985 0.7207,-0.666016 0.0996,-0.28303 0.13015,-0.5695592 0.18945,-0.7480462 a 0.27260457,0.27260457 0 0 0 0.006,-0.01953 c 0.72421,-2.904264 1.44769,-5.808714 2.17188,-8.71288997 a 0.27260457,0.27260457 0 0 0 0.002,-0.0078 c 0.0381,-0.170466 0.0205,-0.342842 -0.0527,-0.482422 -0.0732,-0.13958 -0.1912,-0.235888 -0.3125,-0.296875 -0.24261,-0.121974 -0.50783,-0.134695 -0.74024,-0.09766 l 0.043,-0.0039 z m -5.51562203,0.396484 c -0.106862,8.5e-5 -0.211433,0.04469 -0.287109,0.121094 -0.07466,0.07543 -0.119047995523,0.177082 -0.119140995523,0.285156 a 0.27260457,0.27260457 0 0 0 0,0.002 C 9.1697448e-4,0.92495583 0.04275697,1.0268038 0.12110097,1.1043918 c 0.07961,0.07865 0.184082,0.117505 0.285156,0.117187 H 4.359379 c 0.10046,-8.1e-5 0.20461,-0.03768 0.28516,-0.117187 0.0793,-0.07849 0.11963,-0.18094497 0.12109,-0.28320397 a 0.27260457,0.27260457 0 0 0 0,-0.002 0.27260457,0.27260457 0 0 0 0,-0.002 0.27260457,0.27260457 0 0 0 0,-0.002 c -2.6e-4,-0.05177 -0.0107,-0.101018 -0.0293,-0.148437 a 0.27260457,0.27260457 0 0 0 -0.004,-0.0098 c -0.0211,-0.0483 -0.0505,-0.08928 -0.0859,-0.125 a 0.27260457,0.27260457 0 0 0 0,-0.002 c -0.0338,-0.03386 -0.0775,-0.06562 -0.13086,-0.08789 h -0.002 c -0.0536,-0.02209 -0.10519,-0.03103 -0.15234,-0.03125 a 0.27260457,0.27260457 0 0 0 -0.002,0 z m 6.08984203,0.414063 h 12.80469 c -2.41531,1.75175597 -4.81947,3.51682197 -7.24219,5.25195297 -0.16672,0.07311 -0.23823,0.03745 -0.38867,-0.08203 -0.15035,-0.119405 -0.31263,-0.330498 -0.51172,-0.507813 l -0.0137,-0.01172 C 9.594439,3.9255128 8.046029,2.3749358 6.496069,0.82504783 Z m -0.76172,0.38671797 3.23243,3.232422 c -1.7593,1.658204 -3.51814,3.316441 -5.27735,4.97461 0.68207,-2.735652 1.36277,-5.471457 2.04492,-8.207032 z m 14.14453,0.195313 c -0.66217,2.688405 -1.33356,5.376155 -2.00781,8.0625 -0.78731,-1.629425 -1.57419,-3.259373 -2.36133,-4.888672 1.45616,-1.058159 2.913,-2.115644 4.36914,-3.173828 z M 0.40625697,2.2645008 c -0.109243,-3.38e-4 -0.212466,0.04456 -0.287109,0.11914 -0.07451,0.0746 -0.11948199552,0.177862 -0.119140995523,0.28711 a 0.27260457,0.27260457 0 0 0 0,0.002 C 9.1697448e-4,2.7784718 0.04275697,2.8803198 0.12110097,2.9579068 a 0.27260457,0.27260457 0 0 0 0,0.002 c 0.07802,0.07621 0.182207,0.115511 0.285156,0.115235 H 3.267589 a 0.27260457,0.27260457 0 0 0 0.004,0 c 0.10715,-0.0013 0.20807,-0.04619 0.28125,-0.119141 0.0733,-0.07344 0.1176,-0.174682 0.11914,-0.28125 a 0.27260457,0.27260457 0 0 0 0,-0.0039 c 3e-5,-0.04669 -0.007,-0.100544 -0.0332,-0.160156 -0.0211,-0.0483 -0.0505,-0.08928 -0.0859,-0.125 -0.0379,-0.03813 -0.0877,-0.06916 -0.12695,-0.08594 -0.043,-0.01851 -0.0901,-0.034 -0.15234,-0.03516 a 0.27260457,0.27260457 0 0 0 -0.006,0 z m 0,1.783203 c -0.109233,-3.35e-4 -0.212463,0.04456 -0.287109,0.119141 -0.07458,0.07464 -0.11948199552,0.177861 -0.119140995523,0.287109 a 0.27260457,0.27260457 0 0 0 0,0.0039 C 0.00130697,4.5630708 0.04685697,4.6656098 0.12110097,4.7391038 c 0.07448,0.07358 0.173948,0.11949 0.285156,0.119141 H 1.859382 c 0.111186,3.46e-4 0.210745,-0.04565 0.285156,-0.119141 0.07422,-0.07347 0.119793,-0.175957 0.121094,-0.28125 a 0.27260457,0.27260457 0 0 0 0,-0.002 c 4.38e-4,-0.05329 -0.01158,-0.107876 -0.03125,-0.15625 a 0.27260457,0.27260457 0 0 0 0,-0.002 c -0.02196,-0.05263 -0.0536,-0.09496 -0.08984,-0.130859 -0.03363,-0.03345 -0.07689,-0.06574 -0.130859,-0.08789 -0.05063,-0.02079 -0.103679,-0.03155 -0.15625,-0.03125 z M 9.542979,5.0184068 c 0.55777,0.554552 1.10561,1.128118 1.69336,1.673828 a 0.27260457,0.27260457 0 0 0 0.0215,0.01953 c 0.33273,0.248708 0.72887,0.244845 1.03711,0.128907 0.30494,-0.114698 0.55662,-0.313359 0.76367,-0.476563 l 0.008,-0.0059 1.77735,-1.292969 2.49023,5.1601562 c -0.41562,0.02106 -0.84051,0.01166 -1.30859,0.03125 l 0.0117,-0.002 h -12.0586 c 0.0272,-0.02721 0.0548,-0.04334 0.0801,-0.07422 l -0.0234,0.02734 c 1.83573,-1.7300392 3.67209,-3.4594132 5.50782,-5.1894532 z"/></svg>';
var svg_phone = '<svg height="15px" viewBox="0 0 20.865358 11.079615"><path d="M 6.9785065,0.04532918 C 6.2579362,0.14017463 4.5554781,1.3383693 4.5553055,2.6251192 4.5551319,3.911869 6.4613446,6.6390502 8.0436011,8.1139455 9.625858,9.5888408 12.386522,11.054378 13.553056,11.063692 14.719592,11.072992 16.559007,9.2875562 16.064443,8.5565429 15.569878,7.8255285 13.540505,6.3345674 13.069577,6.6310439 12.598648,6.9275205 12.402783,7.9631693 11.825482,8.3203706 11.248181,8.6775709 10.239289,7.708054 9.4381572,7.1048266 8.6370244,6.5015991 7.149664,4.7952011 7.4131024,4.1978895 7.6765408,3.6005781 9.077928,3.5449484 9.2052524,2.892269 9.3325767,2.2395897 7.6990768,-0.04952027 6.9785065,0.04532918 Z" /></svg>';
var svg_new_contact ='<svg height="15px" viewBox="0 0 20.865358 11.079615" > <path d="m 14.793523,1.2727396 v 3.9583173 h 3.958317 v 1.208104 H 14.793523 V 10.397478 H 13.599631 V 6.4391609 H 9.6413142 V 5.2310569 H 13.599631 V 1.2727396 Z" /> <path d="M 5.8302981,1.0013911 A 2.0503062,2.2045549 0 0 0 3.7788149,3.2052623 2.0503062,2.2045549 0 0 0 5.8302981,5.4091335 2.0503062,2.2045549 0 0 0 7.8788163,3.2052623 2.0503062,2.2045549 0 0 0 5.8302981,1.0013911 Z M 4.0841658,5.4270512 A 3.5779214,7.3094974 0 0 0 2.2757776,10.203897 H 9.233625 A 3.5779214,7.3094974 0 0 0 7.5023158,5.4987218 2.9130581,3.2758681 0 0 1 5.8362271,6.0971715 2.9130581,3.2758681 0 0 1 4.0841658,5.4270512 Z" /> </svg> ';
var svg_exists_contact = '<svg height="15px" viewBox="0 0 20.865358 11.079615" > <path d="m 10.831219,6.7974448 q 0.306508,0 0.463692,0.5029875 0.314367,0.9431016 0.447973,0.9431016 0.102169,0 0.212198,-0.1571836 2.20843,-3.5366309 4.086773,-5.7214829 0.48727,-0.5658609 1.548259,-0.5658609 0.251494,0 0.337945,0.047155 0.08645,0.047155 0.08645,0.1178877 0,0.1100285 -0.259353,0.4322548 -3.033644,3.6466595 -5.627173,7.7019959 -0.180761,0.282931 -0.738763,0.282931 -0.565861,0 -0.66803,-0.04716 Q 10.453979,10.216188 10.092456,9.1316212 9.6837791,7.9291667 9.6837791,7.6226587 q 0,-0.3300856 0.5501429,-0.6365936 0.337944,-0.1886203 0.597297,-0.1886203 z" id="text4" style="font-size:16.0956px;line-height:1.25;stroke-width:0.402389" transform="scale(1.0353084,0.96589577)" aria-label="✓" /> <path d="M 5.8302981,1.0013911 A 2.0503062,2.2045549 0 0 0 3.7788149,3.2052623 2.0503062,2.2045549 0 0 0 5.8302981,5.4091335 2.0503062,2.2045549 0 0 0 7.8788163,3.2052623 2.0503062,2.2045549 0 0 0 5.8302981,1.0013911 Z M 4.0841658,5.4270512 A 3.5779214,7.3094974 0 0 0 2.2757776,10.203897 H 9.233625 A 3.5779214,7.3094974 0 0 0 7.5023158,5.4987218 2.9130581,3.2758681 0 0 1 5.8362271,6.0971715 2.9130581,3.2758681 0 0 1 4.0841658,5.4270512 Z"/> </svg> ';
document.addEventListener("DOMContentLoaded", function () {
var table = new Tabulator("#import_preview", {
maxHeight: "80vh",
groupStartOpen: true,
groupToggleElement:"header",
columns: [
{
"title": "Importare?",
formatter: "rowSelection", titleFormatter: "rowSelection", titleFormatterParams: {
rowRange: "active" //only toggle the values of the active filtered rows
}, hozAlign: "center", headerSort: false
},
{
"title": "",
"field": "import_status",
formatter: function (cell, formatterParams, onRendered) {
switch (cell.getValue()) {
case true:
return svg_new;
case false:
return svg_exists;
case 'warning':
return svg_ambig_contact;
default:
return cell.getValue();
}
}
},
{
"title": "",
"field": "recapito",
"minWidth": 40,
"hozAlign": "center",
"headerWordWrap": false,
"resizable": true,
"sorter": "string",
"headerSort": true,
formatter: function (cell, formatterParams, onRendered) {
switch (cell.getValue()) {
case "email":
return svg_email;
case "telefono":
return svg_phone;
default:
return cell.getValue();
}
}
}, {
"title": "Recapito",
"field": "valore",
"minWidth": 40,
"headerWordWrap": false,
"resizable": true,
"sorter": "string",
"headerSort": true
},
// {
// "title": "Importare?",
// "field": "import",
// "minWidth": 40,
// "headerWordWrap": false,
// "resizable": true,
// "sorter": "boolean",
// formatter: "tickCross",
// "headerSort": true
// }
],
groupBy: function (data) {
return data.nome + " " + data.cognome;
}
});
window.table = table;
table.on("tableBuilt", function () {
table.setData(contatti);
table.setGroupHeader(function (value, count, data, group) {
//value - the value all members of this group share
//count - the number of rows in this group
//data - an array of all the row data objects in this group
//group - the group component for the group
let nc = data[0].nuovo_contatto? svg_new_contact : svg_exists_contact;
return nc + ' '+ value + '<div class="contatto_group">' + ' <span>' + " (" + count + (count > 1 ? " recapiti)" : " recapito)") + '</span><input type="checkbox"></div>'; //return the header contents
});
});
});

View File

@ -0,0 +1 @@
/home/guido/2_external_repo/tabulator/dist/js

View File

@ -0,0 +1,8 @@
{% extends "admin/base_site.html" %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="csv_file" accept=".csv" required>
<button type="submit">Import Contacts</button>
</form>
{% endblock %}

View File

@ -0,0 +1,7 @@
{% extends "admin/change_list.html" %}
{% load admin_urls %}
{% block object-tools-items %}
<li><a href='{% url opts|admin_urlname:"googleimport" %}' class="import_link">Importa contatti da Google</a></li>
{{ block.super }}
{% endblock %}

View File

@ -0,0 +1,27 @@
{% extends "admin/base_site.html" %}
{% load admin_urls static %}
{% block content %}
<form method="post" action="{{app_label}}_{{model_name}}_googleimport_confirm">
{% csrf_token %}
<table id="import_preview">
</table>
<button type="submit">Import Selected</button>
</form>
{% endblock %}
{% block extrastyle %}
{{ block.super }}
<link href="{% static 'admin/css/vendor/tabulator/tabulator.css' %}" rel="stylesheet">
<link href="{% static 'admin/css/import_google_contacts.css' %}" rel="stylesheet">
{% endblock %}
{% block extrahead %}
{{ block.super }}
<script>
var contatti = ({{contacts|safe}});
</script>
<script src="{% static 'admin/js/vendor/tabulator/tabulator.js' %}"></script>
<script src="{% static 'admin/js/import_google_contacts.js' %}"></script>
{% endblock %}

View File

@ -1,20 +1,212 @@
from copy import deepcopy
from django.shortcuts import render, redirect
from django.http import JsonResponse, HttpResponse
from django.utils.http import url_has_allowed_host_and_scheme
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import authenticate, login, logout
from rest_framework import viewsets
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import DjangoModelPermissions, IsAuthenticated
from django_auto_prefetching import AutoPrefetchViewSetMixin
from . import models
from . import serializers
# def index(request):
# return HttpResponse("Hello, %s!" % (request.user.username if request.user.is_authenticated else 'World'))
import csv
import collections
import json
import re
from copy import deepcopy
from io import TextIOWrapper
from contatti_app.models import Email, PersonaFisica, Telefono, Recapito
from dati_geo_app.models import CAP, Comune
from django_auto_prefetching import AutoPrefetchViewSetMixin
from rest_framework import viewsets
from rest_framework.authentication import (BasicAuthentication,
SessionAuthentication)
from rest_framework.permissions import DjangoModelPermissions, IsAuthenticated
from django.contrib import messages
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.forms import AuthenticationForm
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q
from django.http import HttpResponse, JsonResponse
from django.shortcuts import redirect, render
from django.template.response import TemplateResponse
from django.utils.http import url_has_allowed_host_and_scheme
from . import models, serializers
def iterable_but_not_str(obj):
return isinstance(obj, collections.abc.Iterable) and not isinstance(obj, (str, bytes))
def googleimport_preview(request):
def normalizza_generico(*x):
while iterable_but_not_str(x):
if len(x) == 1:
x = x[0]
else:
return tuple(normalizza_generico(y) for y in x)
return x.strip().lower()
normalizza_soggetto = normalizza_generico
normalizza_persona = normalizza_generico
normalizza_email = normalizza_generico
def normalizza_telefono(x):
if iterable_but_not_str(x):
x = x[0]
x=re.sub(r'[^0-9+]', '', x.strip())
if x.startswith('00'):
x='+'+x[2:]
if not x.startswith('+'):
x='+39'+x
return x
csv_file = request.FILES['csv_file']
csv_file_text = TextIOWrapper(csv_file, encoding='utf-8')
reader = csv.DictReader(csv_file_text)
contacts = []
recapiti_db = Recapito.objects.select_related(
'polymorphic_ctype',
'soggetto__personafisica',
'soggetto__personagiuridica'
).filter(
polymorphic_ctype__model__in = {'email', 'pec', 'telefono', }
)
def new_contatto():
return {'emails': set(), 'telefoni': set()}
contatti_db = collections.defaultdict(
lambda: collections.defaultdict(new_contatto))
soggetto_pk_by_recapito = collections.defaultdict(set)
for recapito_valore in recapiti_db:
soggetto = recapito_valore.soggetto
soggetto_pk = soggetto.pk
tipo_sogg = recapito_valore.soggetto.polymorphic_ctype.name
if tipo_sogg == 'persona fisica':
nome = soggetto.personafisica.nome
cognome = soggetto.personafisica.cognome
soggetto_descr = normalizza_persona(nome, cognome)
soggetto_descr_alt = normalizza_soggetto(f'{nome} {cognome}')
contatto = contatti_db[soggetto_descr]
contatti_db[soggetto_descr_alt] = contatto
elif tipo_sogg == 'persona giuridica':
soggetto_descr = normalizza_soggetto(
soggetto.personagiuridica.denominazione)
contatto = contatti_db[soggetto_descr]
else:
raise NotImplementedError
contatto_univoco = contatto[soggetto_pk]
if recapito_valore.polymorphic_ctype.model in {'email', 'pec', }:
recapito_descr = recapito_valore.email.indirizzo_email
contatto_univoco['emails'].add(recapito_descr)
elif recapito_valore.polymorphic_ctype.model in {'telefono', }:
recapito_descr = recapito_valore.telefono.numero
contatto_univoco['telefoni'].add(recapito_descr)
soggetto_pk_by_recapito[recapito_descr] = soggetto_pk
soggetto_pk_by_recapito = dict(soggetto_pk_by_recapito)
contatti_db = {k: dict(v) for k, v in contatti_db.items()}
warning_altro_sogg_emails = set()
warning_altro_sogg_telefoni = set()
contacts = []
for row in reader:
nome = row['Given Name']
cognome = row['Family Name']
contatto = normalizza_persona(nome, cognome)
contatto_alt = normalizza_soggetto(row['Name'])
soggetti_gia_presenti = {k:v for c in (contatto, contatto_alt) if c in contatti_db for k,v in contatti_db[c].items()}
emails = [row[f'E-mail {n} - Value'] for n in range(1, 4)]
emails = [normalizza_email(y)
for x in emails for y in x.split(':::') if y.strip()]
telefoni = [row[f'Phone {n} - Value'] for n in range(1, 4)]
telefoni = [normalizza_telefono(y)
for x in telefoni for y in x.split(':::') if y.strip()]
for recapiti,tipo_recapito in (
(emails,'email',),
(telefoni,'telefono')
):
for recapito_valore in recapiti:
contact = dict()
contacts.append(contact)
contact['recapito']=tipo_recapito
contact['nuovo_contatto'] = not soggetti_gia_presenti
contact['nome'], contact['cognome'] = nome, cognome
contact['full_name'] = contatto_alt
contact['valore']=recapito_valore
if recapito_valore in soggetto_pk_by_recapito:
pk_recapito = soggetto_pk_by_recapito[recapito_valore]
if pk_recapito in soggetti_gia_presenti:
contact['warning']=False
contact['import_status']=False
else:
contact['warning']=True
contact['import_status']='warning'
else:
contact['import_status']=True
# TODO: considerare anche la casistica in cui esiste lo stesso contatto su diversi soggetti
# già direttamente nel file da importare.
# if warning_altro_sogg_emails:
# emails = Email.objects.all().select_related('soggetto')
# emails = {normalizza_email(x['indirizzo_email']): str(
# x.soggetto) for x in emails}
# for soggetto in contacts:
# for r in soggetto:
# if r['recapito'] == 'email' and r['valore'] in emails:
# r['warning_altro_soggetto'] = emails[r['valore']]
# if warning_altro_sogg_telefoni:
# telefoni = Telefono.objects.all().select_related('soggetto')
# telefoni = {normalizza_telefono(x['numero']): str(
# x.soggetto) for x in telefoni}
# for soggetto in contacts:
# for r in soggetto:
# if r['recapito'] == 'telefono' and r['valore'] in telefoni:
# r['warning_altro_soggetto'] = telefoni[r['valore']]
# contacts = [{**{k: v for k, v in soggetto.items() if k != 'recapiti'}, **recapito}
# for soggetto in contacts for recapito in soggetto['recapiti']]
context = {'contacts': json.dumps(contacts)}
return context
def import_google_contacts_confirm(request):
if request.method == 'POST':
selected_indices = [int(key.split('_')[1])
for key in request.POST if key.startswith('import_')]
csv_file = request.session.get('csv_file')
if csv_file and selected_indices:
reader = csv.DictReader(csv_file)
contacts = list(reader)
selected_contacts = [contacts[i] for i in selected_indices]
for row in selected_contacts:
nome = row['Name']
email = row['E-mail 1 - Value']
telefono = row['Phone 1 - Value']
# Importa il soggetto solo se l'utente lo ha selezionato
if nome and row.get('import') == '1':
try:
soggetto = SoggettoContattabile.objects.get(nome=nome)
# Effettua l'aggiornamento del soggetto
soggetto.email = email
soggetto.telefono = telefono
soggetto.save()
messages.success(request, f'Updated contact: {nome}')
except SoggettoContattabile.DoesNotExist:
# Crea un nuovo soggetto
soggetto = SoggettoContattabile.objects.create(
nome=nome)
if email:
Email.objects.create(
soggetto=soggetto, indirizzo_email=email)
if telefono:
Telefono.objects.create(
soggetto=soggetto, numero=telefono)
messages.success(request, f'Imported contact: {nome}')
# Reindirizza a una pagina dopo l'importazione
return redirect('admin')
# Reindirizza se si verifica un errore o non sono stati selezionati contatti
return redirect('import_google_contacts')
# --------------- FINE PREFISSO TEMPLATE ---------------