OpenOffice.org: utilisation des boîtes de dialogue du système par défaut.
[macros-openoffice.git] / auf-poste-client-reflets / openoffice / CODA.py
CommitLineData
b56e39ea
P
1#!
2# -*- coding: utf-8 -*-
4928a69a
P
3"""
4Extension pour faciliter l'intégration du logiciel Coda.
5Macro pour faciliter l'intégration des données provenant de Coda dans
6une feuille de calcul.
7
8Copyright : Agence universitaire de la Francophonie
9Licence : GNU General Public Licence, version 2
10Auteur : Jean Christophe André
11Date de création : septembre 2009
12"""
b56e39ea 13import re
b8c5c6f9 14import datetime
b56e39ea
P
15import uno
16import unohelper
17from com.sun.star.awt import WindowDescriptor
18from com.sun.star.awt.WindowClass import MODALTOP
19from com.sun.star.awt.VclWindowPeerAttribute import OK, DEF_OK
20from com.sun.star.uno import Exception as UnoException, RuntimeException
21from com.sun.star.connection import NoConnectException
22from com.sun.star.lang import Locale, IllegalArgumentException
23from com.sun.star.beans import PropertyValue
24#from com.sun.star.util.ParagraphProperties import CENTER
b8c5c6f9
P
25from com.sun.star.util.NumberFormat import CURRENCY, DATE, NUMBER
26from com.sun.star.i18n.NumberFormatIndex import NUMBER_1000DEC2
b56e39ea
P
27from com.sun.star.container import NoSuchElementException
28
4928a69a
P
29COLONNES_NUMERIQUES = ('Montant doc','Montant EUR')
30
b56e39ea
P
31##############################################################################
32
33def clipboard_data(ctx, mimetype='text/plain;charset=utf-16'):
34 clipboard = ctx.ServiceManager.createInstanceWithContext(
35 "com.sun.star.datatransfer.clipboard.SystemClipboard", ctx)
36 contents = clipboard.getContents()
37 #print "Contents:\n%s\n%s\n%s" % ("-" * 78, "* " + "\n* ".join(dir(contents)), "=" * 78)
38 #flavors = contents.getTransferDataFlavors()
39 #print "Flavors:\n%s\n%s\n%s" % ("-" * 78, "* " + "\n* ".join([flavor.MimeType for flavor in flavors]), "=" * 78)
40 found_flavor = None
41 for flavor in contents.getTransferDataFlavors():
42 if flavor.MimeType == mimetype:
43 found_flavor = flavor
44 break
45 if not found_flavor:
46 raise RuntimeError, u"Erreur : type de données '%s' non disponible.\n" \
47 u"\nAvez-vous bien sélectionné puis copié les données dans CODA ?" \
48 % mimetype
49 data = contents.getTransferData(found_flavor)
50 #print "Data:\n", "-" * 78, "\n", data
51 return data
52
53##############################################################################
54
55def main(ctx, action):
56 data = clipboard_data(ctx)
57
58 numeric_pattern = re.compile('^[+-]?[0-9]+([\. ][0-9][0-9][0-9])*([\.,][0-9]+)?$')
59 date_pattern = re.compile('^([0-9]?[0-9])/([0-9]?[0-9])/([0-9]?[0-9]?[0-9]?[0-9])$')
60
61 if action == 'cumuls':
62 sheet_name = u'CODA-Cumuls'
63 # on découpe le texte reçu en lignes puis en colonnes
64 # on ne garde ici que les 10 premières colonnes
65 data = [row.split('\t')[:10] for row in data.splitlines()]
66 data[0][6] = '' # suppression de la 2nde colonne 'Engagement'
67 data[0][8] = '' # suppression de la colonne 'Colonne9'
68
b8c5c6f9 69 elif action == 'details' or action == 'details-encore':
b56e39ea
P
70 sheet_name = u'CODA-Détails'
71 # on découpe le texte reçu en lignes puis en colonnes
72 data = [row.split('\t') for row in data.splitlines()]
73
74 else:
75 raise RuntimeError, u"Action inconnue (erreur de programmation)."
76
77 # on efface la dernière ligne pleine de car. nuls, le cas échéant
78 last = data[len(data)-1]
79 if len(last) == 1 and last[0].strip('\x00') == '':
80 del data[len(data)-1]
81
b8c5c6f9
P
82 # on détermine le type des données, par colonne :
83 # - on vérifie si toutes les valeurs de la colonne sont identiques
84 # - on vérifie si la colonne ne contient que des dates
85 # - on vérifie si la colonne ne contient que des nombres
86 column_type = [str] * len(data[0])
b56e39ea 87 for column in range(len(data[0])-1,-1,-1):
b8c5c6f9
P
88 # quelques colonnes ne doivent pas être testées
89 if action in ('details','details-encore') and data[0][column] in ('Num doc','PCG'):
90 continue
91 # on prend la première valeur, nettoyée un peu pour l'analyse
92 first = data[1][column].strip().replace(u'\xa0','')
93 flag_date = first and re.match(date_pattern, first)
94 flag_numeric = first and first != '#DEV!' and re.match(numeric_pattern, first)
b56e39ea 95 diff = False
b8c5c6f9
P
96 # parcours des autres valeurs de la colonne
97 for row in range(2,len(data)):
98 value = data[row][column].strip().replace(u'\xa0','')
99 if value:
100 if not diff and value != first:
101 diff = True
102 if flag_date and not re.match(date_pattern, value):
103 flag_date = False
104 if flag_numeric and value != '#DEV!' and not re.match(numeric_pattern, value):
105 flag_numeric = False
b56e39ea
P
106 # s'il y a au moins 2 lignes et que les valeurs dans la colonne
107 # sont toutes identiques et qu'il n'y a pas d'intitulé sur la
108 # colonne alors on supprime toute la colonne
109 if len(data) > 2 and not diff and not data[0][column]:
110 for row in range(len(data)):
111 del data[row][column]
b8c5c6f9
P
112 del column_type[column]
113 continue
114 # si la colonne contient des dates, conversion des valeurs comme telles
115 if flag_date:
116 for row in range(1,len(data)):
117 value = data[row][column].strip().replace(u'\xa0','')
118 if value and value != '#DEV!':
119 m = re.match(date_pattern, value)
120 if m:
121 (day, month, year) = [int(x) for x in m.groups()]
122 if year < 50:
123 year += 2000
124 elif year < 100:
125 year += 1900
126 value = '%02d/%02d/%04d' % (month, day, year)
127 data[row][column] = value
128 column_type[column] = datetime.date
129 continue
130 # si la colonne est numérique, conversion des valeurs comme telles
131 if flag_numeric:
132 for row in range(1,len(data)):
133 value = data[row][column].strip().replace(u'\xa0','')
134 if value and value != '#DEV!':
135 data[row][column] = float(value.replace('.','').replace(',','.'))
136 column_type[column] = float
137 continue
b56e39ea
P
138
139 #print "Data:\n", "-" * 78, "\n", data
140
141 # récupération du document en cours, vérification du type Spreadsheet
142 desktop = ctx.ServiceManager.createInstanceWithContext(
143 "com.sun.star.frame.Desktop", ctx)
144 document = desktop.getCurrentComponent()
145 if not document.supportsService("com.sun.star.sheet.SpreadsheetDocument"):
146 raise RuntimeError, u"Ce n'est pas un document de type Spreadsheet (Calc)."
147
148 # basculement vers la feuille de calcul concernée, création au besoin
149 sheets = document.getSheets()
150 try:
151 sheet = sheets.getByName(sheet_name)
152 new_sheet = False
153 except NoSuchElementException, e:
154 sheets.insertNewByName(sheet_name, 0)
155 sheet = sheets.getByName(sheet_name)
156 new_sheet = True
157 controller = document.getCurrentController()
158 controller.setActiveSheet(sheet)
159
b8c5c6f9 160 # récupération des formats date, monétaire et nombre en français
a69abe13
P
161 # http://api.openoffice.org/docs/common/ref/com/sun/star/util/XNumberFormatTypes.html
162 # http://api.openoffice.org/docs/common/ref/com/sun/star/i18n/NumberFormatIndex.html
b56e39ea
P
163 locale_fr_FR = Locale('fr', 'FR', '')
164 number_formats = document.getNumberFormats()
b56e39ea 165 date_format = number_formats.getStandardFormat(DATE, locale_fr_FR)
b8c5c6f9
P
166 currency_format = number_formats.getStandardFormat(CURRENCY, locale_fr_FR)
167 number_format = number_formats.getFormatIndex(NUMBER_1000DEC2, locale_fr_FR)
168 #format_string = number_formats.generateFormat(
169 # NUMBER, locale_fr_FR, True, False, 2, 1)
170 #format_key = number_formats.queryKey(format_string, locale_fr_FR, True)
171 #if format_key < 0:
172 # format_key = number_formats.addNew(format_string, locale_fr_FR)
173 #print "NUMBER_1000DEC2=%s key=%s" % (NUMBER_1000DEC2, format_key)
174 #number_format = number_formats.getByKey(format_key)
175
176 # détermination de la zone de travail
177 if action.endswith('-encore'):
178 # localisation de la la zone déjà utilisée
179 cursor = sheet.createCursor()
180 cursor.gotoEndOfUsedArea(False)
181 cursor.gotoStartOfUsedArea(True)
182 rangeAddress = cursor.getRangeAddress()
183 row = rangeAddress.EndRow
184 # FIXME: on devrait comparer ici rangeAddress.EndColumn avec len(data[0])
185 else:
186 row = 0
b56e39ea 187
b8c5c6f9
P
188 cell_range = sheet.getCellRangeByPosition(
189 0, row, len(data[0])-1, row + len(data)-1)
b56e39ea
P
190 cursor = sheet.createCursorByRange(cell_range)
191
192 # insertion des données
193 for row in range(1, len(data)):
194 for column in range(len(data[row])):
195 value = data[row][column]
196 cell = cursor.getCellByPosition(column, row)
b8c5c6f9 197 if column_type[column] == float and value != '#DEV!':
b56e39ea 198 cell.Value = value
b56e39ea 199 else:
b56e39ea 200 cell.Formula = value
b8c5c6f9 201
b56e39ea 202 # insertion des en-têtes, après les données pour un autofit correct
b8c5c6f9
P
203 if new_sheet:
204 for column in range(len(data[0])):
205 # écriture de l'en-tête de la colonne
206 cell = cursor.getCellByPosition(column, 0)
207 cell.Formula = data[0][column]
208 # formatage par défaut pour la colonne
209 sheet_column = sheet.Columns.getByIndex(column)
210 if column_type[column] == datetime.date:
211 sheet_column.NumberFormat = date_format
212 elif column_type[column] == float:
213 if action == 'cumuls':
214 sheet_column.NumberFormat = currency_format
215 else:
4928a69a 216 if data[0][column] in COLONNES_NUMERIQUES:
b8c5c6f9
P
217 sheet_column.NumberFormat = number_format
218 else:
219 sheet_column.NumberFormat = currency_format
220 # ajustement automatique, une fois toutes les valeurs écrites
221 sheet_column.OptimalWidth = True
222 # formatage spécifique pour la ligne d'en-têtes
223 headers_row = sheet.Rows.getByIndex(0)
224 headers_row.ParaAdjust = 3 # CENTER
b56e39ea
P
225
226##############################################################################
227
228# Show a message box with the UNO based toolkit
229def messageBox(ctx, message):
230 document = XSCRIPTCONTEXT.getDocument()
231 window = document.CurrentController.Frame.ContainerWindow
232
233 aDescriptor = WindowDescriptor()
234 aDescriptor.Type = MODALTOP
235 aDescriptor.WindowServiceName = "infobox"
236 aDescriptor.ParentIndex = -1
237 aDescriptor.Parent = window
238 #aDescriptor.Bounds = Rectangle()
239 aDescriptor.WindowAttributes = OK
240
241 tk = window.getToolkit()
242 msgbox = tk.createWindow(aDescriptor)
243 msgbox.setCaptionText("Collage d'interrogation CODA")
244 msgbox.setMessageText(unicode(message))
245
246 return msgbox.execute()
247
248def coller_cumuls(event=False):
249 u"""Coller une interrogation des cumuls CODA."""
250 ctx = uno.getComponentContext()
251 try:
252 main(ctx, 'cumuls')
253 except Exception, e:
254 messageBox(ctx, e)
255 return None
256
257def coller_details(event=False):
258 u"""Coller une interrogation des détails CODA."""
259 ctx = uno.getComponentContext()
260 try:
261 main(ctx, 'details')
262 except Exception, e:
263 messageBox(ctx, e)
264 return None
265
b8c5c6f9
P
266def coller_details_encore(event=False):
267 u"""Coller une interrogation des détails CODA supplémentaire."""
268 ctx = uno.getComponentContext()
269 try:
270 main(ctx, 'details-encore')
271 except Exception, e:
272 messageBox(ctx, e)
273 return None
274
275g_exportedScripts = (coller_cumuls, coller_details, coller_details_encore)
b56e39ea
P
276
277##############################################################################
278
279from com.sun.star.task import XJobExecutor
280
281class CollerCumulsJob(unohelper.Base, XJobExecutor):
282 def __init__(self, context):
283 self._context = context
284
285 def trigger(self, args):
286 try:
287 main(self._context, 'cumuls')
288 except Exception, e:
289 messageBox(self._context, e)
290
291class CollerDetailsJob(unohelper.Base, XJobExecutor):
292 def __init__(self, context):
293 self._context = context
294
295 def trigger(self, args):
296 try:
297 main(self._context, 'details')
298 except Exception, e:
299 messageBox(self._context, e)
300
b8c5c6f9
P
301class CollerDetailsEncoreJob(unohelper.Base, XJobExecutor):
302 def __init__(self, context):
303 self._context = context
304
305 def trigger(self, args):
306 try:
307 main(self._context, 'details-encore')
308 except Exception, e:
309 messageBox(self._context, e)
310
b56e39ea
P
311g_ImplementationHelper = unohelper.ImplementationHelper()
312g_ImplementationHelper.addImplementation( \
313 CollerCumulsJob, "org.auf.openoffice.CODA.CollerCumuls", \
314 ("com.sun.star.task.Job",),)
315g_ImplementationHelper.addImplementation( \
316 CollerDetailsJob, "org.auf.openoffice.CODA.CollerDetails", \
317 ("com.sun.star.task.Job",),)
b8c5c6f9
P
318g_ImplementationHelper.addImplementation( \
319 CollerDetailsEncoreJob, "org.auf.openoffice.CODA.CollerDetailsEncore", \
320 ("com.sun.star.task.Job",),)