Statistics
| Revision:

root / tmp / org.txm.analec.rcp / src / org / txm / macro / urs / edit / Frpos2CategorieMacro.groovy @ 1217

History | View | Annotate | Download (8.6 kB)

1
// @author Bruno Oberlé (2017-04-01 21:50)
2

    
3
/*
4
Définit la catégorie grammaticale du maillon d'après le champ `frpos'
5
(tagset de TreeTagger).  Le script est adapté de
6
http://svn.code.sf.net/p/txm/code/trunk/plugins/Analec/AnalecRCP/src/org/txm/macro/analec/Fropos2CategorieMacro.groovy.
7

8
Voici la liste des catégories grammaticales retenues (manuel d'annotation de
9
Democrat):
10
- GN: Groupe Nominal (le petit chat, le chat, le même, ce chat etc.)
11
- POSS: Possessif (mon, ton son, ma, ta, sa, mes, tes, ses, notre, votre,
12
  leur, nos, vos, leurs)
13
- PR: Pronom (moi, toi, lui, elle, nous, vous, eux, elles, le tien, le mien,
14
  moi-même etc.)
15
- PR_CL_O: Pronom Clitique Objet (me, te, le, la, les, lui, leur, y, en)
16
- PR_CL_R: Pronom Clitique Réfléchi
17
- PR_CL_S: Pronom Clitique Sujet (je, tu, il, elle, on, nous, vous, ils,
18
  elles)
19
- PR_REL: Pronom Relatif (qui, que, quoi, dont, où, lequel, quiconque etc.)
20
- PR_WH: Pronom Interrogatif (qui, que, quoi, lequel etc.)
21

22
Le script ne peut pas désambiguïser les pronoms clitiques de même forme
23
(`nous' est-il un sujet, un objet ou un réfléchi?).  Dans ce cas, le script
24
opte pour le sujet (ou pour l'objet si l'ambiguïté n'est que entre objet et
25
réfléchi).
26

27
Quand il n'y a aucune information disponible (erreurs de l'étiqueteur), la
28
valeur est UNDEFINED.
29

30
L'algorithme est décrit ici:
31
https://groupes.renater.fr/wiki/democrat/prive/txm_annotation_exploitation
32

33
*/
34

    
35
package org.txm.macro.urs.edit
36

    
37
import org.apache.commons.lang.*
38
import org.kohsuke.args4j.*
39
import groovy.transform.*
40
import org.txm.*
41
import org.txm.rcp.swt.widget.parameters.*
42
import org.txm.annotation.urs.*
43
import org.txm.searchengine.cqp.*
44
import org.txm.searchengine.cqp.corpus.*
45
import visuAnalec.Message.*
46
import visuAnalec.donnees.*
47
import visuAnalec.elements.*
48
import visuAnalec.vue.*
49

    
50
def testClitic(def position, def frpos) {
51

    
52
   // je me sers de la forme, parce qu'il est difficile de savoir quel est le
53
   // lemme de "elle" ("il"?), de "te" ("tu"?) ou encore de "leur"
54
   def form = CQI.cpos2Str(word.getQualifiedName(), position)[0].toLowerCase()
55
   if (     form == "je" || form == "j'"
56
         || form == "tu" || form == "t'"
57
         || form == "il"
58
         || form == "elle"
59
         || form == "on"
60
         || form == "vous"
61
         || form == "nous"
62
         || form == "ils"
63
         || form == "elles" ) {
64
      return "PR_CL_S"
65
   } else if (form == "me" || form == "m'"
66
           || form == "te"
67
           || form == "le" || form == "l'"
68
           || form == "la"
69
           || form == "lui"
70
           || form == "leur"
71
           || form == "les" ) {
72
      return "PR_CL_O"
73
   } else if (form == "se" || form == "s'") {
74
      return "PR_CL_R"
75
   }
76
   return null
77

    
78
}
79

    
80
def testPhrase(def positions, def Mention) {
81

    
82
   // on doit regarder ce qui apparaît en premier:
83
   // - ce peut être un nom, comme dans `le petit chat que j'ai adopté'
84
   // - ce peut être un pronom relatif, comme dans `(le livre) dans lequel
85
   // j'ai lu cette histoire...'
86
   // NOTE: dans Democrat, on n'annote pas, bizarrement, la relative dans le
87
   // maillon, donc, dans un GN on n'a jamais de relatif inclus.  On aura donc
88
   // toujours `[le petit chat] [que] [j']ai adopté'.  Mais tout le monde
89
   // n'annote pas de la sorte...
90
   for (def i=0; i<Mention.length; i++) {
91
         def mention = Mention[i]
92
         //def form = CQI.cpos2Str(word.getQualifiedName(), positions[i])[0]
93
         if (mention == "NOM" || mention == "NAM") {
94
            return "GN"
95
         } else if (mention == "PRO:REL") {
96
            return "PR_REL"
97
         }
98
    }
99

    
100
    return null
101

    
102
}
103

    
104
def testWhPronoun(position, mention) {
105
   def form = CQI.cpos2Str(word.getQualifiedName(), position)[0]
106
   if (mention == "PRO" && (form == "qui" || form == "que" || form == "lequel")) {
107
      return "PR_WH"
108
   }
109
   return null
110

    
111
}
112

    
113
def testRules(def positions, def Mention) {
114
        def catégorie = null
115

    
116
   // a possessive (mon, ma...)
117
   if (Mention.length == 1 && Mention.contains("DET:POS"))
118
      catégorie = "POSS"
119

    
120
   // a clitic (subject: je, tu...; object: me, te; reflexive: se)
121
   if (!catégorie && Mention.length == 1 && Mention.contains("PRO:PER"))
122
      catégorie = testClitic(positions[0], Mention[0])
123

    
124
   // an interrogative pronoun
125
   if (!catégorie && Mention.length == 1)
126
      catégorie = testWhPronoun(positions[0], Mention[0])
127

    
128
   // a noun phrase or a relative pronoun
129
   if (!catégorie)
130
      catégorie = testPhrase(positions, Mention)
131

    
132
   // some other kind of pronouns
133
   if (!catégorie
134
         && (   Mention.contains("PRO")
135
             || Mention.contains("PRO:POSS")
136
             || Mention.contains("PRO:IND")
137
             || Mention.contains("PRO:DEM")
138
             || Mention.contains("PRO:PER") )
139
         && !Mention.contains("NOM")
140
         && !Mention.contains("NAM") )
141
      catégorie = "PRO"
142

    
143
// Fin des règles, aucune n'a matchée. On stocke le pattern  qu'on affichera à la fin.
144
   if (!catégorie) {
145
      catégorie = "UNDEFINED" // clear the field
146
                def forms = CQI.cpos2Str(word.getQualifiedName(), positions)
147
                if (!errors.containsKey(Mention)) errors[Mention] = new HashSet()
148
                errors[Mention] << forms
149
        }
150
        
151
        return catégorie
152
}
153

    
154
//
155
// FIN DE LA DÉFINITION DES RÈGLES
156
//
157

    
158
// CORPS DU SCRIPT
159

    
160
if (!(corpusViewSelection instanceof MainCorpus)) {
161
        println "Corpora selection is not a Corpus"
162
        return
163
}
164

    
165
// BEGINNING OF PARAMETERS
166
@Field @Option(name="unit_type", usage="", widget="String", required=true, def="MENTION")
167
def unit_type
168
@Field @Option(name="pos_property_name", usage="", widget="String", required=true, def="pos")
169
def pos_property_name
170
@Field @Option(name="reset", usage="", widget="Boolean", required=true, def="true")
171
def reset
172
if (!ParametersDialog.open(this)) return
173

    
174
corpus = corpusViewSelection
175
CQI = CQPSearchEngine.getCqiClient()
176
word = corpus.getWordProperty()
177
posProperty = corpus.getProperty(pos_property_name)
178
if (posProperty == null) {
179
        println "Error: CQP corpus does not contains the word property with name=$pos_property_name"
180
        return
181
}
182
analecCorpus = URSCorpora.getCorpus(corpus)
183
vue = URSCorpora.getVue(corpus)
184
structure = analecCorpus.getStructure()
185
if (!structure.getUnites().contains(unit_type)) { // check if the structure contains the unit_type units
186
        println "Error: corpus structure does not contains unit with name=$unit_type"
187
        return
188
}
189

    
190
CATEGORIE = "CATEGORIE"
191
// Si la structure d'annotation ne contient pas CATEGORIE, on la crée avec ses valeurs
192
if (!structure.getUniteProperties(unit_type).contains(CATEGORIE)) { 
193

    
194
// FIXME: dans le script original (see also
195
// http://forge.cbp.ens-lyon.fr/redmine/issues/2065), on utilise
196
// analecCorpus.ajouterProp/Val, mais cela ne marche pas dans ma version de
197
// TXM-Analec --> je retourne donc à structure.ajouterProp/Val
198

    
199
// la propriété
200
        structure.ajouterProp(Unite.class, unit_type, CATEGORIE)
201
// les valeurs
202
        structure.ajouterVal(Unite.class, unit_type, CATEGORIE, "GN")
203
        structure.ajouterVal(Unite.class, unit_type, CATEGORIE, "POSS")
204
        structure.ajouterVal(Unite.class, unit_type, CATEGORIE, "PRO")
205
        structure.ajouterVal(Unite.class, unit_type, CATEGORIE, "PR_CL_O")
206
        structure.ajouterVal(Unite.class, unit_type, CATEGORIE, "PR_CL_S")
207
        structure.ajouterVal(Unite.class, unit_type, CATEGORIE, "PR_CL_R")
208
        structure.ajouterVal(Unite.class, unit_type, CATEGORIE, "PR_REL")
209
        structure.ajouterVal(Unite.class, unit_type, CATEGORIE, "PR_WH")
210
//...
211
}
212

    
213
def nModified = 0
214
def nIgnored = 0
215

    
216
errors = new HashMap()
217
def units = analecCorpus.getUnites(unit_type)
218
units.sort() { a, b -> a.getDeb() <=> b.getDeb() ?: a.getFin() <=> b.getFin() }
219
for (Unite unit : units) { // process all units
220
        
221
        def prop = unit.getProp(CATEGORIE)
222
        if (!reset && prop != null && prop.length() > 0) continue // l'unité a déjà une CATEGORIE
223
        
224
        int[] positions = null
225
        if (unit.getDeb() == unit.getFin()) positions = [unit.getDeb()]
226
        else positions = (unit.getDeb()..unit.getFin())
227
        
228
        def Mention = CQI.cpos2Str(posProperty.getQualifiedName(), positions)
229
        def cat = testRules(positions, Mention)
230

    
231
        if (cat != null) {
232
                // following line in the original script but doesn't work for me:
233
      // vue.setValeurChamp(unit, CATEGORIE, cat)
234
      unit.getProps().put(CATEGORIE, cat)
235
                nModified++
236
        } else {
237
                nIgnored++
238
        }
239
}
240

    
241
println "Result:"
242
println "- $nModified units of type $unit_type have been modified."
243
println "- $nIgnored units of type $unit_type have not been modified.\n"
244

    
245
if (errors.size() > 0) {
246
        println "Some rules should be added to this macro to process the following remaining 'FROPOS / words' values:"
247
        errors.keySet().each { println "fropos="+it+"\twords="+errors[it].join(" | ") }
248
}
249

    
250
// udpate the view (also see also
251
// http://forge.cbp.ens-lyon.fr/redmine/issues/2065)
252
URSCorpora.getVue(analecCorpus).retablirVueParDefaut()