Statistics
| Revision:

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()