Coverage for wizards / lox_restore_wizard.py: 37%
81 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-28 01:16 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-28 01:16 +0000
1# -*- coding: utf-8 -*-
3from odoo import models, fields, api, _
4from odoo.exceptions import UserError, ValidationError
5import logging
7_logger = logging.getLogger(__name__)
10class LoxRestoreWizard(models.TransientModel):
11 _name = 'lox.restore.wizard'
12 _description = 'LOX Backup Restore Wizard'
14 CONFIRMATION_TEXT = 'RESTORE'
16 config_id = fields.Many2one(
17 'lox.backup.config',
18 string='Configuration',
19 required=True,
20 default=lambda self: self._default_config_id(),
21 )
22 backup_uuid = fields.Char(
23 string='Backup UUID',
24 required=True,
25 readonly=True,
26 )
27 backup_name = fields.Char(
28 string='Backup Name',
29 readonly=True,
30 )
31 backup_date = fields.Datetime(
32 string='Backup Date',
33 readonly=True,
34 )
35 backup_size = fields.Char(
36 string='Backup Size',
37 readonly=True,
38 )
39 backup_component = fields.Char(
40 string='Component',
41 readonly=True,
42 )
43 backup_status = fields.Char(
44 string='Status',
45 readonly=True,
46 )
48 # Confirmation field
49 confirmation_text = fields.Char(
50 string='Confirmation',
51 required=True,
52 help='Type RESTORE to confirm the restoration',
53 )
55 # Display fields
56 warning_message = fields.Html(
57 string='Warning',
58 compute='_compute_warning_message',
59 )
61 @api.model
62 def _default_config_id(self):
63 config = self.env['lox.backup.config'].search([
64 ('active', '=', True),
65 ('source_registered', '=', True),
66 ], limit=1)
67 return config.id if config else False
69 @api.depends('backup_uuid')
70 def _compute_warning_message(self):
71 for record in self:
72 record.warning_message = '''
73 <div style="background-color: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 16px; margin-bottom: 16px;">
74 <p style="font-weight: bold; color: #991b1b; margin: 0 0 8px 0;">
75 ⚠️ Warning: This action is irreversible!
76 </p>
77 <p style="color: #991b1b; margin: 0;">
78 This will overwrite your current Odoo data. All current database records,
79 filestore attachments, and configurations will be replaced with the backup data.
80 This action cannot be undone.
81 </p>
82 </div>
83 '''
85 @api.constrains('confirmation_text')
86 def _check_confirmation_text(self):
87 for record in self:
88 if record.confirmation_text and record.confirmation_text.upper().strip() != self.CONFIRMATION_TEXT:
89 raise ValidationError(
90 _('Please type %s to confirm the restore.') % self.CONFIRMATION_TEXT
91 )
93 @api.model
94 def create_for_backup(self, backup_uuid, backup_info=None):
95 """Create a wizard pre-populated with backup info"""
96 values = {
97 'backup_uuid': backup_uuid,
98 }
100 if backup_info:
101 values['backup_name'] = backup_info.get('name', backup_uuid[:8] + '...')
102 values['backup_date'] = backup_info.get('created_at')
103 values['backup_component'] = backup_info.get('component', 'full')
104 values['backup_status'] = backup_info.get('status', 'unknown')
106 # Format size
107 size_bytes = backup_info.get('size', backup_info.get('size_bytes', 0))
108 if size_bytes:
109 if size_bytes > 1024 * 1024 * 1024:
110 values['backup_size'] = f"{size_bytes / (1024*1024*1024):.2f} GB"
111 elif size_bytes > 1024 * 1024:
112 values['backup_size'] = f"{size_bytes / (1024*1024):.2f} MB"
113 elif size_bytes > 1024:
114 values['backup_size'] = f"{size_bytes / 1024:.2f} KB"
115 else:
116 values['backup_size'] = f"{size_bytes} bytes"
118 return self.create(values)
120 def action_confirm_restore(self):
121 """Request restore after confirmation"""
122 self.ensure_one()
124 # Validate confirmation text
125 if self.confirmation_text.upper().strip() != self.CONFIRMATION_TEXT:
126 raise UserError(
127 _('Please type %s to confirm the restore.') % self.CONFIRMATION_TEXT
128 )
130 config = self.config_id
131 if not config.source_registered:
132 raise UserError(_('Source is not registered. Please register first.'))
134 api = self.env['lox.api'].create_client(config)
136 try:
137 # Request restore from LOX API
138 result = api.request_restore(self.backup_uuid)
140 if result.get('success') or result.get('task_id'):
141 # Log the restore request
142 self.env['lox.backup.log'].create({
143 'config_id': config.id,
144 'backup_uuid': self.backup_uuid,
145 'component': 'restore',
146 'status': 'pending',
147 'started_at': fields.Datetime.now(),
148 'notes': _('Restore requested for backup %s') % self.backup_uuid,
149 })
151 return {
152 'type': 'ir.actions.client',
153 'tag': 'display_notification',
154 'params': {
155 'title': _('Restore Requested'),
156 'message': _(
157 'Restore has been requested for backup %s. '
158 'You will be notified when the backup is ready for download and restoration.'
159 ) % (self.backup_uuid[:8] + '...'),
160 'type': 'success',
161 'sticky': True,
162 'next': {'type': 'ir.actions.act_window_close'},
163 }
164 }
165 else:
166 raise UserError(
167 _('Failed to request restore: %s') % result.get('error', 'Unknown error')
168 )
170 except UserError:
171 raise
172 except Exception as e:
173 _logger.exception('Restore request failed')
174 raise UserError(_('Restore request failed: %s') % str(e))
176 def action_cancel(self):
177 """Cancel the wizard"""
178 return {'type': 'ir.actions.act_window_close'}
181class LoxBackupLogRestore(models.Model):
182 """Extend backup log to add restore action"""
183 _inherit = 'lox.backup.log'
185 def action_request_restore(self):
186 """Open restore confirmation wizard"""
187 self.ensure_one()
189 if not self.backup_uuid:
190 raise UserError(_('No backup UUID available for this log entry.'))
192 if self.status not in ['completed', 'validated']:
193 raise UserError(_('Can only restore completed backups.'))
195 # Get backup info from API
196 api = self.env['lox.api'].create_client(self.config_id)
197 backup_info = api.get_backup(self.backup_uuid)
199 wizard = self.env['lox.restore.wizard'].create_for_backup(
200 self.backup_uuid,
201 backup_info.get('data', {}) if backup_info.get('success') else None
202 )
204 return {
205 'name': _('Confirm Restore'),
206 'type': 'ir.actions.act_window',
207 'res_model': 'lox.restore.wizard',
208 'res_id': wizard.id,
209 'view_mode': 'form',
210 'target': 'new',
211 'context': {'dialog_size': 'medium'},
212 }