root / tmp / org.txm.analec.rcp / src / org / txm / macro / urs / edit / Frpos2CategorieDemocratMacro.groovy @ 1217
History | View | Annotate | Download (8.6 kB)
1 | 671 | mdecorde | // @author Bruno Oberlé (2017-04-01 21:50)
|
---|---|---|---|
2 | 671 | mdecorde | |
3 | 671 | mdecorde | /*
|
4 | 671 | mdecorde | Définit la catégorie grammaticale du maillon d'après le champ `frpos'
|
5 | 671 | mdecorde | (tagset de TreeTagger). Le script est adapté de
|
6 | 671 | mdecorde | http://svn.code.sf.net/p/txm/code/trunk/plugins/Analec/AnalecRCP/src/org/txm/macro/analec/Fropos2CategorieMacro.groovy.
|
7 | 671 | mdecorde | |
8 | 671 | mdecorde | Voici la liste des catégories grammaticales retenues (manuel d'annotation de
|
9 | 671 | mdecorde | Democrat):
|
10 | 671 | mdecorde | - GN: Groupe Nominal (le petit chat, le chat, le même, ce chat etc.)
|
11 | 671 | mdecorde | - POSS: Possessif (mon, ton son, ma, ta, sa, mes, tes, ses, notre, votre,
|
12 | 671 | mdecorde | leur, nos, vos, leurs)
|
13 | 671 | mdecorde | - PR: Pronom (moi, toi, lui, elle, nous, vous, eux, elles, le tien, le mien,
|
14 | 671 | mdecorde | moi-même etc.)
|
15 | 671 | mdecorde | - PR_CL_O: Pronom Clitique Objet (me, te, le, la, les, lui, leur, y, en)
|
16 | 671 | mdecorde | - PR_CL_R: Pronom Clitique Réfléchi
|
17 | 671 | mdecorde | - PR_CL_S: Pronom Clitique Sujet (je, tu, il, elle, on, nous, vous, ils,
|
18 | 671 | mdecorde | elles)
|
19 | 671 | mdecorde | - PR_REL: Pronom Relatif (qui, que, quoi, dont, où, lequel, quiconque etc.)
|
20 | 671 | mdecorde | - PR_WH: Pronom Interrogatif (qui, que, quoi, lequel etc.)
|
21 | 671 | mdecorde | |
22 | 671 | mdecorde | Le script ne peut pas désambiguïser les pronoms clitiques de même forme
|
23 | 671 | mdecorde | (`nous' est-il un sujet, un objet ou un réfléchi?). Dans ce cas, le script
|
24 | 671 | mdecorde | opte pour le sujet (ou pour l'objet si l'ambiguïté n'est que entre objet et
|
25 | 671 | mdecorde | réfléchi).
|
26 | 671 | mdecorde | |
27 | 671 | mdecorde | Quand il n'y a aucune information disponible (erreurs de l'étiqueteur), la
|
28 | 671 | mdecorde | valeur est UNDEFINED.
|
29 | 671 | mdecorde | |
30 | 671 | mdecorde | L'algorithme est décrit ici:
|
31 | 671 | mdecorde | https://groupes.renater.fr/wiki/democrat/prive/txm_annotation_exploitation
|
32 | 671 | mdecorde | |
33 | 671 | mdecorde | */
|
34 | 671 | mdecorde | |
35 | 1217 | mdecorde | package org.txm.macro.urs.edit
|
36 | 671 | mdecorde | |
37 | 671 | mdecorde | import org.apache.commons.lang.* |
38 | 671 | mdecorde | import org.kohsuke.args4j.* |
39 | 671 | mdecorde | import groovy.transform.* |
40 | 671 | mdecorde | import org.txm.* |
41 | 671 | mdecorde | import org.txm.rcp.swt.widget.parameters.* |
42 | 1217 | mdecorde | import org.txm.annotation.urs.* |
43 | 671 | mdecorde | import org.txm.searchengine.cqp.* |
44 | 671 | mdecorde | import org.txm.searchengine.cqp.corpus.* |
45 | 671 | mdecorde | import visuAnalec.Message.* |
46 | 671 | mdecorde | import visuAnalec.donnees.* |
47 | 671 | mdecorde | import visuAnalec.elements.* |
48 | 671 | mdecorde | import visuAnalec.vue.* |
49 | 671 | mdecorde | |
50 | 671 | mdecorde | def testClitic(def position, def frpos) { |
51 | 671 | mdecorde | |
52 | 671 | mdecorde | // je me sers de la forme, parce qu'il est difficile de savoir quel est le
|
53 | 671 | mdecorde | // lemme de "elle" ("il"?), de "te" ("tu"?) ou encore de "leur"
|
54 | 671 | mdecorde | def form = CQI.cpos2Str(word.getQualifiedName(), position)[0].toLowerCase() |
55 | 671 | mdecorde | if ( form == "je" || form == "j'" |
56 | 671 | mdecorde | || form == "tu" || form == "t'" |
57 | 671 | mdecorde | || form == "il"
|
58 | 671 | mdecorde | || form == "elle"
|
59 | 671 | mdecorde | || form == "on"
|
60 | 671 | mdecorde | || form == "vous"
|
61 | 671 | mdecorde | || form == "nous"
|
62 | 671 | mdecorde | || form == "ils"
|
63 | 671 | mdecorde | || form == "elles" ) {
|
64 | 671 | mdecorde | return "PR_CL_S" |
65 | 671 | mdecorde | } else if (form == "me" || form == "m'" |
66 | 671 | mdecorde | || form == "te"
|
67 | 671 | mdecorde | || form == "le" || form == "l'" |
68 | 671 | mdecorde | || form == "la"
|
69 | 671 | mdecorde | || form == "lui"
|
70 | 671 | mdecorde | || form == "leur"
|
71 | 671 | mdecorde | || form == "les" ) {
|
72 | 671 | mdecorde | return "PR_CL_O" |
73 | 671 | mdecorde | } else if (form == "se" || form == "s'") { |
74 | 671 | mdecorde | return "PR_CL_R" |
75 | 671 | mdecorde | } |
76 | 671 | mdecorde | return null |
77 | 671 | mdecorde | |
78 | 671 | mdecorde | } |
79 | 671 | mdecorde | |
80 | 671 | mdecorde | def testPhrase(def positions, def Mention) { |
81 | 671 | mdecorde | |
82 | 671 | mdecorde | // on doit regarder ce qui apparaît en premier:
|
83 | 671 | mdecorde | // - ce peut être un nom, comme dans `le petit chat que j'ai adopté'
|
84 | 671 | mdecorde | // - ce peut être un pronom relatif, comme dans `(le livre) dans lequel
|
85 | 671 | mdecorde | // j'ai lu cette histoire...'
|
86 | 671 | mdecorde | // NOTE: dans Democrat, on n'annote pas, bizarrement, la relative dans le
|
87 | 671 | mdecorde | // maillon, donc, dans un GN on n'a jamais de relatif inclus. On aura donc
|
88 | 671 | mdecorde | // toujours `[le petit chat] [que] [j']ai adopté'. Mais tout le monde
|
89 | 671 | mdecorde | // n'annote pas de la sorte...
|
90 | 671 | mdecorde | for (def i=0; i<Mention.length; i++) { |
91 | 671 | mdecorde | def mention = Mention[i]
|
92 | 671 | mdecorde | //def form = CQI.cpos2Str(word.getQualifiedName(), positions[i])[0]
|
93 | 671 | mdecorde | if (mention == "NOM" || mention == "NAM") { |
94 | 671 | mdecorde | return "GN" |
95 | 671 | mdecorde | } else if (mention == "PRO:REL") { |
96 | 671 | mdecorde | return "PR_REL" |
97 | 671 | mdecorde | } |
98 | 671 | mdecorde | } |
99 | 671 | mdecorde | |
100 | 671 | mdecorde | return null |
101 | 671 | mdecorde | |
102 | 671 | mdecorde | } |
103 | 671 | mdecorde | |
104 | 671 | mdecorde | def testWhPronoun(position, mention) { |
105 | 671 | mdecorde | def form = CQI.cpos2Str(word.getQualifiedName(), position)[0] |
106 | 671 | mdecorde | if (mention == "PRO" && (form == "qui" || form == "que" || form == "lequel")) { |
107 | 671 | mdecorde | return "PR_WH" |
108 | 671 | mdecorde | } |
109 | 671 | mdecorde | return null |
110 | 671 | mdecorde | |
111 | 671 | mdecorde | } |
112 | 671 | mdecorde | |
113 | 671 | mdecorde | def testRules(def positions, def Mention) { |
114 | 671 | mdecorde | def catégorie = null |
115 | 671 | mdecorde | |
116 | 671 | mdecorde | // a possessive (mon, ma...)
|
117 | 671 | mdecorde | if (Mention.length == 1 && Mention.contains("DET:POS")) |
118 | 671 | mdecorde | catégorie = "POSS" |
119 | 671 | mdecorde | |
120 | 671 | mdecorde | // a clitic (subject: je, tu...; object: me, te; reflexive: se)
|
121 | 671 | mdecorde | if (!catégorie && Mention.length == 1 && Mention.contains("PRO:PER")) |
122 | 671 | mdecorde | catégorie = testClitic(positions[0], Mention[0]) |
123 | 671 | mdecorde | |
124 | 671 | mdecorde | // an interrogative pronoun
|
125 | 671 | mdecorde | if (!catégorie && Mention.length == 1) |
126 | 671 | mdecorde | catégorie = testWhPronoun(positions[0], Mention[0]) |
127 | 671 | mdecorde | |
128 | 671 | mdecorde | // a noun phrase or a relative pronoun
|
129 | 671 | mdecorde | if (!catégorie) |
130 | 671 | mdecorde | catégorie = testPhrase(positions, Mention)
|
131 | 671 | mdecorde | |
132 | 671 | mdecorde | // some other kind of pronouns
|
133 | 671 | mdecorde | if (!catégorie |
134 | 671 | mdecorde | && ( Mention.contains("PRO")
|
135 | 671 | mdecorde | || Mention.contains("PRO:POSS")
|
136 | 671 | mdecorde | || Mention.contains("PRO:IND")
|
137 | 671 | mdecorde | || Mention.contains("PRO:DEM")
|
138 | 671 | mdecorde | || Mention.contains("PRO:PER") )
|
139 | 671 | mdecorde | && !Mention.contains("NOM")
|
140 | 671 | mdecorde | && !Mention.contains("NAM") )
|
141 | 671 | mdecorde | catégorie = "PRO" |
142 | 671 | mdecorde | |
143 | 671 | mdecorde | // Fin des règles, aucune n'a matchée. On stocke le pattern qu'on affichera à la fin.
|
144 | 671 | mdecorde | if (!catégorie) { |
145 | 671 | mdecorde | catégorie = "UNDEFINED" // clear the field |
146 | 671 | mdecorde | def forms = CQI.cpos2Str(word.getQualifiedName(), positions)
|
147 | 671 | mdecorde | if (!errors.containsKey(Mention)) errors[Mention] = new HashSet() |
148 | 671 | mdecorde | errors[Mention] << forms |
149 | 671 | mdecorde | } |
150 | 671 | mdecorde | |
151 | 671 | mdecorde | return catégorie |
152 | 671 | mdecorde | } |
153 | 671 | mdecorde | |
154 | 671 | mdecorde | //
|
155 | 671 | mdecorde | // FIN DE LA DÉFINITION DES RÈGLES
|
156 | 671 | mdecorde | //
|
157 | 671 | mdecorde | |
158 | 671 | mdecorde | // CORPS DU SCRIPT
|
159 | 671 | mdecorde | |
160 | 671 | mdecorde | if (!(corpusViewSelection instanceof MainCorpus)) { |
161 | 671 | mdecorde | println "Corpora selection is not a Corpus"
|
162 | 671 | mdecorde | return
|
163 | 671 | mdecorde | } |
164 | 671 | mdecorde | |
165 | 671 | mdecorde | // BEGINNING OF PARAMETERS
|
166 | 671 | mdecorde | @Field @Option(name="unit_type", usage="", widget="String", required=true, def="MENTION") |
167 | 671 | mdecorde | def unit_type
|
168 | 671 | mdecorde | @Field @Option(name="pos_property_name", usage="", widget="String", required=true, def="pos") |
169 | 671 | mdecorde | def pos_property_name
|
170 | 671 | mdecorde | @Field @Option(name="reset", usage="", widget="Boolean", required=true, def="true") |
171 | 671 | mdecorde | def reset
|
172 | 671 | mdecorde | if (!ParametersDialog.open(this)) return |
173 | 671 | mdecorde | |
174 | 671 | mdecorde | corpus = corpusViewSelection |
175 | 786 | sjacqu01 | CQI = CQPSearchEngine.getCqiClient() |
176 | 671 | mdecorde | word = corpus.getWordProperty() |
177 | 671 | mdecorde | posProperty = corpus.getProperty(pos_property_name) |
178 | 671 | mdecorde | if (posProperty == null) { |
179 | 671 | mdecorde | println "Error: CQP corpus does not contains the word property with name=$pos_property_name"
|
180 | 671 | mdecorde | return
|
181 | 671 | mdecorde | } |
182 | 1217 | mdecorde | analecCorpus = URSCorpora.getCorpus(corpus) |
183 | 1217 | mdecorde | vue = URSCorpora.getVue(corpus) |
184 | 671 | mdecorde | structure = analecCorpus.getStructure() |
185 | 671 | mdecorde | if (!structure.getUnites().contains(unit_type)) { // check if the structure contains the unit_type units |
186 | 671 | mdecorde | println "Error: corpus structure does not contains unit with name=$unit_type"
|
187 | 671 | mdecorde | return
|
188 | 671 | mdecorde | } |
189 | 671 | mdecorde | |
190 | 671 | mdecorde | CATEGORIE = "CATEGORIE"
|
191 | 671 | mdecorde | // Si la structure d'annotation ne contient pas CATEGORIE, on la crée avec ses valeurs
|
192 | 671 | mdecorde | if (!structure.getUniteProperties(unit_type).contains(CATEGORIE)) {
|
193 | 671 | mdecorde | |
194 | 671 | mdecorde | // FIXME: dans le script original (see also
|
195 | 671 | mdecorde | // http://forge.cbp.ens-lyon.fr/redmine/issues/2065), on utilise
|
196 | 671 | mdecorde | // analecCorpus.ajouterProp/Val, mais cela ne marche pas dans ma version de
|
197 | 671 | mdecorde | // TXM-Analec --> je retourne donc à structure.ajouterProp/Val
|
198 | 671 | mdecorde | |
199 | 671 | mdecorde | // la propriété
|
200 | 671 | mdecorde | structure.ajouterProp(Unite.class, unit_type, CATEGORIE) |
201 | 671 | mdecorde | // les valeurs
|
202 | 671 | mdecorde | structure.ajouterVal(Unite.class, unit_type, CATEGORIE, "GN")
|
203 | 671 | mdecorde | structure.ajouterVal(Unite.class, unit_type, CATEGORIE, "POSS")
|
204 | 671 | mdecorde | structure.ajouterVal(Unite.class, unit_type, CATEGORIE, "PRO")
|
205 | 671 | mdecorde | structure.ajouterVal(Unite.class, unit_type, CATEGORIE, "PR_CL_O")
|
206 | 671 | mdecorde | structure.ajouterVal(Unite.class, unit_type, CATEGORIE, "PR_CL_S")
|
207 | 671 | mdecorde | structure.ajouterVal(Unite.class, unit_type, CATEGORIE, "PR_CL_R")
|
208 | 671 | mdecorde | structure.ajouterVal(Unite.class, unit_type, CATEGORIE, "PR_REL")
|
209 | 671 | mdecorde | structure.ajouterVal(Unite.class, unit_type, CATEGORIE, "PR_WH")
|
210 | 671 | mdecorde | //...
|
211 | 671 | mdecorde | } |
212 | 671 | mdecorde | |
213 | 671 | mdecorde | def nModified = 0 |
214 | 671 | mdecorde | def nIgnored = 0 |
215 | 671 | mdecorde | |
216 | 671 | mdecorde | errors = new HashMap() |
217 | 671 | mdecorde | def units = analecCorpus.getUnites(unit_type)
|
218 | 671 | mdecorde | units.sort() { a, b -> a.getDeb() <=> b.getDeb() ?: a.getFin() <=> b.getFin() } |
219 | 671 | mdecorde | for (Unite unit : units) { // process all units |
220 | 671 | mdecorde | |
221 | 671 | mdecorde | def prop = unit.getProp(CATEGORIE)
|
222 | 671 | mdecorde | if (!reset && prop != null && prop.length() > 0) continue // l'unité a déjà une CATEGORIE |
223 | 671 | mdecorde | |
224 | 671 | mdecorde | int[] positions = null |
225 | 671 | mdecorde | if (unit.getDeb() == unit.getFin()) positions = [unit.getDeb()]
|
226 | 671 | mdecorde | else positions = (unit.getDeb()..unit.getFin())
|
227 | 671 | mdecorde | |
228 | 671 | mdecorde | def Mention = CQI.cpos2Str(posProperty.getQualifiedName(), positions)
|
229 | 671 | mdecorde | def cat = testRules(positions, Mention)
|
230 | 671 | mdecorde | |
231 | 671 | mdecorde | if (cat != null) { |
232 | 671 | mdecorde | // following line in the original script but doesn't work for me:
|
233 | 671 | mdecorde | // vue.setValeurChamp(unit, CATEGORIE, cat)
|
234 | 671 | mdecorde | unit.getProps().put(CATEGORIE, cat) |
235 | 671 | mdecorde | nModified++ |
236 | 671 | mdecorde | } else {
|
237 | 671 | mdecorde | nIgnored++ |
238 | 671 | mdecorde | } |
239 | 671 | mdecorde | } |
240 | 671 | mdecorde | |
241 | 671 | mdecorde | println "Result:"
|
242 | 671 | mdecorde | println "- $nModified units of type $unit_type have been modified."
|
243 | 671 | mdecorde | println "- $nIgnored units of type $unit_type have not been modified.\n"
|
244 | 671 | mdecorde | |
245 | 671 | mdecorde | if (errors.size() > 0) { |
246 | 671 | mdecorde | println "Some rules should be added to this macro to process the following remaining 'FROPOS / words' values:"
|
247 | 671 | mdecorde | errors.keySet().each { println "fropos="+it+"\twords="+errors[it].join(" | ") } |
248 | 671 | mdecorde | } |
249 | 671 | mdecorde | |
250 | 671 | mdecorde | // udpate the view (also see also
|
251 | 671 | mdecorde | // http://forge.cbp.ens-lyon.fr/redmine/issues/2065)
|
252 | 1217 | mdecorde | URSCorpora.getVue(analecCorpus).retablirVueParDefaut() |