Statistics
| Revision:

root / tmp / org.txm.analec.rcp / src / org / txm / macro / analec / Frpos2CategorieMacro.groovy @ 1166

History | View | Annotate | Download (8.6 kB)

1 481 mdecorde
// @author Bruno Oberlé (2017-04-01 21:50)
2 481 mdecorde
3 481 mdecorde
/*
4 481 mdecorde
Définit la catégorie grammaticale du maillon d'après le champ `frpos'
5 481 mdecorde
(tagset de TreeTagger).  Le script est adapté de
6 481 mdecorde
http://svn.code.sf.net/p/txm/code/trunk/plugins/Analec/AnalecRCP/src/org/txm/macro/analec/Fropos2CategorieMacro.groovy.
7 481 mdecorde

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

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

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

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

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