Projet

Général

Profil

0002_anonymisation_keep_archiving.patch

Jérôme Schneider, 03 avril 2014 14:06

Télécharger (11,4 ko)

Voir les différences:


  

wcs/admin/forms.py
27 27
except ImportError:
28 28
    M2Crypto = None
29 29

  
30
import datetime
30 31
import tarfile
31 32
import time
32 33
from cStringIO import StringIO
......
206 207

  
207 208
class FormDefPage(Directory):
208 209
    _q_exports = ['', 'fields', 'delete', 'duplicate', 'export',
209
                  'archive', 'invite', 'enable', 'workflow', 'category',
210
                  'role', ('workflow-options', 'workflow_options'),
210
                  'anonymise', 'archive', 'invite', 'enable', 'workflow',
211
                  'category', 'role', ('workflow-options', 'workflow_options'),
211 212
                  ('workflow-status-remapping', 'workflow_status_remapping'),
212 213
                  'roles', 'title', 'options', ('acl-read', 'acl_read')]
213 214

  
......
386 387
        r += htmltext('<li><a href="duplicate">%s</a></li>') % _('Duplicate')
387 388
        if ET:
388 389
            r += htmltext('<li><a href="export">%s</a></li>') % _('Export')
390
        r += htmltext('<li><a href="anonymise">%s</a></li>') % _('Anonymise handled forms')
389 391
        if not (get_publisher().has_site_option('postgresql') and get_cfg('postgresql', {})):
390 392
            r += htmltext('<li><a href="archive">%s</a></li>') % _('Archive')
391 393
        if self.formdef.roles:
......
847 849
            'attachment; filename=%s-archive.wcs' % self.formdef.url_name)
848 850
        return job.file_content
849 851

  
852
    def anonymise(self):
853
        all_forms = self.formdef.data_class().select()
854
        for formdata in all_forms:
855
            if not formdata.anonymised:
856
                formdata.anonymise()
857
        if get_request().form.get('job'):
858
            return self.anonymise_processing()
859

  
860
        endpoints = []
861
        for status in self.formdef.workflow.get_endpoint_status():
862
            endpoints.append((status.id, status.name))
863

  
864
        form = Form(enctype='multipart/form-data')
865

  
866
        form.widgets.append(HtmlWidget('<p>%s</p>' % _(
867
            "You are about to irrevocably anonymise forms which are closed.")))
868

  
869

  
870
        form.add(DateWidget, 'before_request_date',
871
                title=_('Requests ended before'),
872
                value=datetime.date.today() - datetime.timedelta(30),
873
                required=True)
874
        form.add(CheckboxesWidget, 'endpoints', title=_('Status of the requests to anonymise'),
875
                value=[endpoint[0] for endpoint in endpoints],
876
                elements=endpoints,
877
                inline=False,
878
                required=True)
879

  
880
        form.add_submit('submit', _('Submit'))
881
        form.add_submit('cancel', _('Cancel'))
882

  
883
        if form.get_widget('cancel').parse():
884
            return redirect('.')
885

  
886
        if not form.is_submitted() or form.has_errors():
887
            get_response().breadcrumb.append(('anonymise', _('Anonymise')))
888
            html_top('forms', title=_('Anonymise Forms'))
889
            r = TemplateIO(html=True)
890
            r += htmltext('<h2>%s</h2>') % _('Anonymise Forms')
891
            r += form.render()
892
            return r.getvalue()
893
        else:
894
            return self.anonymise_submit(form)
895

  
896
    def anonymise_submit(self, form):
897

  
898
        class Anonymiser:
899

  
900
            def __init__(self, formdef, status_ids, before_date):
901
                self.formdef = formdef
902
                self.status_ids = ["wf-%s" % id for id in status_ids]
903
                self.before_date = before_date
904

  
905
            def anonymise(self, job=None):
906
                all_forms = self.formdef.data_class().select()
907
                all_forms = [x for x in all_forms if x.status in self.status_ids]
908
                all_forms = [x for x in all_forms if (
909
                    x.evolution and x.evolution[-1].time < self.before_date) or (
910
                    x.receipt_time < self.before_date)]
911
                for formdata in all_forms:
912
                    if not formdata.anonymised:
913
                        formdata.anonymise()
914

  
915
        before_date = form.get_widget('before_request_date').parse()
916
        before_date = time.strptime(before_date, misc.date_format())
917
        status_ids = form.get_widget('endpoints').parse()
918
        count = self.formdef.data_class().count()
919
        anonymiser = Anonymiser(self.formdef, status_ids, before_date)
920
        if count > 100: # Arbitrary threshold
921
            job = get_response().add_after_job(
922
                str(N_('Anonymising forms')),
923
                anonymiser.anonymise)
924
            return redirect('anonymise?job=%s' % job.id)
925
        else:
926
            anonymiser.anonymise()
927

  
928
        return redirect('.')
929

  
930
    def anonymise_processing(self):
931
        try:
932
            job = AfterJob.get(get_request().form.get('job'))
933
        except KeyError:
934
            return redirect('.')
935

  
936
        html_top('forms', title=_('Anonymising'))
937
        r = TemplateIO(html=True)
938
        r += get_session().display_message()
939
        get_response().add_javascript(['jquery.js', 'afterjob.js'])
940
        r += htmltext('<dl class="job-status">')
941
        r += htmltext('<dt>')
942
        r += _(job.label)
943
        r += htmltext('</dt>')
944
        r += htmltext('<dd>')
945
        r += htmltext('<span class="afterjob" id="%s">') % job.id
946
        r += _(job.status)
947
        r += htmltext('</span>')
948
        r += htmltext('</dd>')
949
        r += htmltext('</dl>')
950

  
951
        r += htmltext('<div class="done">')
952
        r += htmltext('<a href="./">%s</a>') % _('Back')
953
        r += htmltext('</div>')
954
        return r.getvalue()
955

  
850 956
    def invite(self):
851 957
        if not self.formdef.roles:
852 958
            return template.error_page(
wcs/backoffice/root.py
317 317
        fields.append(FakeField('user-label', 'user-label', _('User Label')))
318 318
        fields.extend(self.formdef.fields)
319 319
        fields.append(FakeField('status', 'status', _('Status')))
320
        fields.append(FakeField('anonymised', 'anonymised', _('Anonymised')))
320 321

  
321 322
        return fields
322 323

  
wcs/fields.py
118 118
    prefill = None
119 119
    store_display_value = None
120 120

  
121
    anonymise = True
121 122
    stats = None
122 123

  
123 124
    def __init__(self, **kwargs):
......
554 555

  
555 556
    widget_class = CheckboxWidget
556 557
    required = False
558
    anonymise = False
557 559

  
558 560
    def perform_more_widget_changes(self, form, kwargs, edit = True):
559 561
        if not edit:
......
633 635
    key = 'file'
634 636
    description = N_('File Upload')
635 637

  
638
    anonymise = False
636 639
    widget_class = FileWithPreviewWidget
637 640

  
638 641
    def get_view_value(self, value):
......
765 768

  
766 769
    items = []
767 770
    show_as_radio = False
771
    anonymise = False
768 772
    widget_class = SingleSelectHintWidget
769 773
    data_source = {}
770 774

  
......
1368 1372
    items = []
1369 1373
    randomize_items = False
1370 1374
    widget_class = RankedItemsWidget
1375
    anonymise = False
1371 1376

  
1372 1377
    def perform_more_widget_changes(self, form, kwargs, edit = True):
1373 1378
        kwargs['elements'] = self.items or []
wcs/formdata.py
15 15
# along with this program; if not, see <http://www.gnu.org/licenses/>.
16 16

  
17 17
import copy
18
import datetime
18 19
import json
19 20
import time
20 21

  
......
130 131
    user_hash = None
131 132
    receipt_time = None
132 133
    status = None
134
    anonymised = None
133 135
    page_no = None # page to use when restoring from draft
134 136
    evolution = None
135 137
    data = None
......
452 454

  
453 455
        return status_action_roles
454 456

  
457
    def anonymise(self):
458
        for field in self.formdef.fields:
459
            if field.anonymise:
460
                self.data[field.id] = None
461

  
462
        self.anonymised = datetime.datetime.now()
463
        self.user_id = None
464
        self.user_hash = None
465
        self.editable_by = None
466
        self._signature = None
467
        self.workflow_data = None
468
        self.workflow_roles = None
469

  
470
        for evo in self.evolution:
471
            evo.who = None
472
            evo.parts = None
473
            evo.comment = None
474
            evo.parts = None
475
        self.store()
476

  
455 477
    def export_to_json(self):
456 478
        data = {}
457 479
        data['id'] = '%s/%s' % (self.formdef.url_name, self.id)
wcs/forms/backoffice.py
226 226
                        r += htmltext('<td class="cell-user cell-no-user">-</td>')
227 227
                elif f.type == 'status':
228 228
                    r += htmltext('<td class="cell-status">%s</td>') % filled.get_status_label()
229
                elif f.type == 'anonymised':
230
                    anonymised = _('No')
231
                    if filled.anonymised:
232
                        anonymised = _('Yes')
233
                    r += htmltext('<td class="cell-anonymised">%s</td>') % anonymised
229 234
                else:
230 235
                    r += htmltext('<td>')
231 236
                    value = filled.data.get('%s_display' % f.id, filled.data.get(f.id))
wcs/sql.py
112 112
                                    user_id varchar,
113 113
                                    user_hash varchar,
114 114
                                    receipt_time timestamp,
115
                                    anonymised timestamptz,
115 116
                                    status varchar,
116 117
                                    page_no varchar,
117 118
                                    workflow_data bytea,
......
131 132

  
132 133
    needed_fields = set(['id', 'user_id', 'user_hash', 'receipt_time',
133 134
        'status', 'workflow_data', 'id_display', 'fts', 'page_no',
134
        'workflow_roles', 'workflow_roles_array'])
135
        'anonymised', 'workflow_roles', 'workflow_roles_array'])
135 136

  
136 137
    # migrations
137 138
    if not 'fts' in existing_fields:
......
147 148
    if not 'page_no' in existing_fields:
148 149
        cur.execute('''ALTER TABLE %s ADD COLUMN page_no varchar''' % table_name)
149 150

  
151
    if not 'anonymised' in existing_fields:
152
        cur.execute('''ALTER TABLE %s ADD COLUMN anonymised timestamptz''' % table_name)
153

  
150 154
    # add new fields
151 155
    for field in formdef.fields:
152 156
        assert field.id is not None
......
449 453
        ('receipt_time', 'timestamp'),
450 454
        ('status', 'varchar'),
451 455
        ('page_no', 'varchar'),
456
        ('anonymised', 'timestamptz'),
452 457
        ('workflow_data', 'bytea'),
453 458
        ('id_display', 'varchar'),
454 459
        ('workflow_roles', 'bytea'),
......
506 511
                'page_no': self.page_no,
507 512
                'workflow_data': bytearray(cPickle.dumps(self.workflow_data)),
508 513
                'id_display': self.id_display,
514
                'anonymised': self.anonymised,
509 515
        }
510 516
        if self.receipt_time:
511 517
            sql_dict['receipt_time'] = datetime.datetime.fromtimestamp(time.mktime(self.receipt_time)),