Coverage for portality/tasks/article_bulk_delete.py: 97%
78 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-19 18:38 +0100
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-19 18:38 +0100
1from copy import deepcopy
2import json
4from flask_login import current_user
6from portality import models
8from portality.tasks.redis_huey import main_queue
9from portality.decorators import write_required
10from portality.util import batch_up
12from portality.background import AdminBackgroundTask, BackgroundApi, BackgroundException, BackgroundSummary
15def article_bulk_delete_manage(selection_query, dry_run=True):
17 if dry_run:
18 ArticleBulkDeleteBackgroundTask.check_admin_privilege(current_user.id)
19 estimate = ArticleBulkDeleteBackgroundTask.estimate_delete_counts(selection_query)
20 return BackgroundSummary(None, affected={"articles" : estimate})
22 ids = ArticleBulkDeleteBackgroundTask.resolve_selection_query(selection_query)
23 job = ArticleBulkDeleteBackgroundTask.prepare(
24 current_user.id,
25 selection_query=selection_query,
26 ids=ids
27 )
28 ArticleBulkDeleteBackgroundTask.submit(job)
30 affected = len(ids)
31 job_id = None
32 if job is not None:
33 job_id = job.id
34 return BackgroundSummary(job_id, affected={"articles" : affected})
37class ArticleBulkDeleteBackgroundTask(AdminBackgroundTask):
39 BATCH_SIZE = 1000
41 __action__ = "article_bulk_delete"
43 @classmethod
44 def _job_parameter_check(cls, params):
45 # we definitely need "ids" defined
46 return bool(cls.get_param(params, 'ids'))
48 def run(self):
49 """
50 Execute the task as specified by the background_job
51 :return:
52 """
53 job = self.background_job
54 params = job.params
56 ids = self.get_param(params, 'ids')
58 if not self._job_parameter_check(params):
59 raise BackgroundException("{}.run run without sufficient parameters".format(self.__class__.__name__))
61 batches_count = len(ids) // self.BATCH_SIZE + (0 if len(ids) % self.BATCH_SIZE == 0 else 1)
62 job.add_audit_message("About to delete {} articles in {} batches".format(len(ids), batches_count))
64 for batch_num, batch in enumerate(batch_up(ids, self.BATCH_SIZE), start=1):
65 article_delete_q_by_ids = models.Article.make_query(should_terms={'_id': batch}, consistent_order=False)
66 models.Article.delete_selected(query=article_delete_q_by_ids, snapshot=True)
67 job.add_audit_message("Deleted {} articles in batch {} of {}".format(len(batch), batch_num, batches_count))
69 job.add_audit_message("Deleted {} articles".format(len(ids)))
71 def cleanup(self):
72 """
73 Cleanup after a successful OR failed run of the task
74 :return:
75 """
76 pass
78 @classmethod
79 def estimate_delete_counts(cls, selection_query):
80 q = deepcopy(selection_query)
81 res = models.Article.query(q=q)
82 return res['hits']['total']['value']
84 @classmethod
85 def resolve_selection_query(cls, selection_query):
86 q = deepcopy(selection_query)
87 q["_source"] = False
88 iterator = models.Article.iterate(q=q, page_size=5000, wrap=False)
89 return [j['_id'] for j in iterator]
91 @classmethod
92 def prepare(cls, username, **kwargs):
93 """
94 Take an arbitrary set of keyword arguments and return an instance of a BackgroundJob,
95 or fail with a suitable exception
97 :param kwargs: arbitrary keyword arguments pertaining to this task type
98 :return: a BackgroundJob instance representing this task
99 """
101 super(ArticleBulkDeleteBackgroundTask, cls).prepare(username, **kwargs)
103 # first prepare a job record
104 job = models.BackgroundJob()
105 job.user = username
106 job.action = cls.__action__
107 refs = {}
108 cls.set_reference(refs, "selection_query", json.dumps(kwargs['selection_query']))
109 job.reference = refs
111 params = {}
112 cls.set_param(params, 'ids', kwargs['ids'])
114 if not cls._job_parameter_check(params):
115 raise BackgroundException("{}.prepare run without sufficient parameters".format(cls.__name__))
117 job.params = params
119 return job
121 @classmethod
122 def submit(cls, background_job):
123 """
124 Submit the specified BackgroundJob to the background queue
126 :param background_job: the BackgroundJob instance
127 :return:
128 """
129 background_job.save(blocking=True)
130 article_bulk_delete.schedule(args=(background_job.id,), delay=10)
133@main_queue.task()
134@write_required(script=True)
135def article_bulk_delete(job_id):
136 job = models.BackgroundJob.pull(job_id)
137 task = ArticleBulkDeleteBackgroundTask(job)
138 BackgroundApi.execute(task)