Utilisateur:Botomatik/Scripts/de-tab-cas.py
Apparence
Ce script réalise les actions suivantes :
- Remplacement de
{{de-tab-cas}}
par{{de-nom}}
, si le format des paramètres de{{de-tab-cas}}
est celui attendu
- Si ce remplacement a eu lieu :
- Remplacement de
{{de-nom}}
par un de ses dérivés - Placement du tableau de flexions avant la ligne de forme s’il est placé après
- Retrait du pluriel entre parenthèses dans la ligne de forme s’il est déjà présent dans la boite de flexion (si une prononciation est présente sur la ligne de forme, le bot laisse tel quel)
- Remplacement de // par
{{pron}}
sur la ligne de forme - Ajout du modèle
{{pron}}
à la ligne de forme lorsqu’absent (pour les lignes de forme simples du type'''xxx''' {{genre}}
)
# -*- coding: utf-8 -*-
'''
Ce script remplace le modèle {{de-tab-cas}} par
{{de-nom}} ou ses dérivés
'''
import re
import codecs
from wikipedia import *
import pagegenerators
import webbrowser
from pywikibot import i18n
Test = False # Pour tester le script
edit_fileerror = True # Pour mettre à jour le fichier d'erreur qd une page n'est pas modifiée
editcounter = 0 # Nombres de pages traitées
pages_traitees = [] # Liste des pages traitées (modifiées ou listées comme erreur)
pages_modifiees = [] # Liste des pages modifiées
# Fréquence de mise à jour des listes de pages à traiter et traitées
frqMajListes = 100
# Fichier contenant la liste des pages à modifier
fichier_entree = u'_Entrées\de-tab-cas.txt'
# Pour lister les pages non modifiées à cause d'une erreur
fichier_erreurs = u'_Sorties\Erreurs de-tab-cas.txt'
# Pour lister les pages modifiées
fichier_sortie = u'_Sorties\Modifiées de-tab-cas.txt'
def main(page):
'''
Fonction éditant une page avec de-tab-cas pour le remplacer par de-nom
'''
summary = u'Bot: Remplacement : {{de-tab-cas}} → {{de-nom}}'
titre = page.title()
if Test:
output('\n' + titre)
try:
contenu = page.get()
except wikipedia.NoPage:
non_modifiee(titre, msg=u'Pas de telle page')
return
except wikipedia.LockedPage:
non_modifiee(titre, msg=u'Page bloquée en écriture')
return
except wikipedia.IsRedirectPage:
non_modifiee(titre, msg=u'Page de redirection')
return
except wikipedia.ServerError:
non_modifiee(titre, msg=u'ServerError pour .get()')
return
except wikipedia.BadTitle:
non_modifiee(titre, msg=u'BadTitle pour .get()')
return
except wikipedia.EditConflict:
non_modifiee(titre, msg=u'EditConflict pour .get()')
return
# Fonction de remplacement de {{de-nom}} en un modèle plus spécifique
def deNomRepl(match):
# Nom du modèle de remplacement
modele = ''
genre = match.group(1)
cas = {
# strip() pour éradiquer d'éventuelles tabulations avant le terme
'ns': match.group(2).strip(),
'np': match.group(3).strip(),
'as': match.group(4).strip(),
'ap': match.group(5).strip(),
'gs': match.group(6).strip(),
'gp': match.group(7).strip(),
'ds': match.group(8).strip(),
'dp': match.group(9).strip()
}
def plurEndWithSuffix(suffix):
'''
Est-ce que toutes les formes du pluriel ont la même graphie que le
nominatif singulier suffixé par "suffix" ?
'''
if cas['ap'] == cas['np'] and cas['np'] == cas['dp'] and cas['dp'] == cas['gp'] and cas['gp'] == cas['ns'] + suffix:
return True
else:
return False
def singEndWithSuffix(suffix):
''' Idem, mais pour les 3 singuliers (excepté 'ns') '''
if cas['as'] == cas['ds'] and cas['ds'] == cas['gs'] and cas['gs'] == cas['ns'] + suffix:
return True
else:
return False
def endWithSuffix(list, suffix):
'''
Est-ce que toutes les formes du tableau liste ont la même graphie que le
nominatif singulier suffixé par "suffix" ?
@type list: Liste de chaines de caractères ('ns', 'ap',...)
'''
for elt in list:
if cas[elt] == cas['ns'] + suffix:
continue
else:
return False
return True
def equalToNs(list):
'''
Toutes les formes de la liste donnée sont-elles identique au nominatif singulier ?
'''
for elt in list:
if cas[elt] == cas['ns']:
continue
else:
return False
return True
if genre == 'f':
if plurEndWithSuffix('nen') and singEndWithSuffix('') and cas['ns'][-2:] == 'in':
modele = 'de-nom-f-in'
elif plurEndWithSuffix('en') and singEndWithSuffix(''):
modele = 'de-nom-f-en'
elif plurEndWithSuffix('n') and singEndWithSuffix(''):
modele = 'de-nom-f-n'
if genre == 'm':
if plurEndWithSuffix('en') and singEndWithSuffix('en'):
modele = 'de-nom-m-en'
elif plurEndWithSuffix('n') and singEndWithSuffix('n'):
modele = 'de-nom-m-n'
elif equalToNs(['as', 'ds', 'np', 'ap', 'gp']) and cas['gs'] == cas['ns'] + 's' and \
cas['dp'] == cas['ns'] + 'n' and cas['ns'][-2:] == 'er':
modele = 'de-nom-m-er'
elif equalToNs(['as', 'ds']) and cas['gs'] == cas['ns'] + 's' and \
endWithSuffix(['np', 'ap', 'gp'], 'e') and cas['dp'] == cas['ns'] + 'en':
modele = 'de-nom-m-s-e'
elif plurEndWithSuffix('en') and equalToNs(['as', 'ds']) and cas['gs'] == cas['ns'] + 's':
modele = 'de-nom-m-s-en'
elif plurEndWithSuffix('s') and equalToNs(['as', 'ds']) and cas['gs'] == cas['ns'] + 's':
modele = 'de-nom-m-s-s'
if genre == 'n':
if equalToNs(['as', 'ds']) and cas['gs'] == cas['ns'] + 's' and \
endWithSuffix(['np', 'ap', 'gp'], 'e') and cas['dp'] == cas['ns'] + 'en':
modele = 'de-nom-n-s-e'
elif equalToNs(['ns', 'ds']) and cas['gs'] == cas['ns'] + 's' and plurEndWithSuffix('') and cas['ns'][-4:] == 'chen':
modele = 'de-nom-n-chen'
elif equalToNs(['as', 'ds']) and cas['gs'] == cas['ns'] + 'ses' and \
endWithSuffix(['np', 'ap', 'gp'], 'se') and cas['dp'] == cas['ns'] + 'sen' and cas['ns'][-2:] == 'is':
modele = 'de-nom-n-is'
elif equalToNs(['as', 'ds']) and cas['gs'] == cas['ns'] + 's' and plurEndWithSuffix('') and cas['ns'][-2:] == 'en':
modele = 'de-nom-n-en'
elif equalToNs(['as', 'ds']) and cas['gs'] == cas['ns'] + 's' and plurEndWithSuffix('en'):
modele = 'de-nom-n-s-en'
if modele != '':
if cas['ns'] == titre:
return '{{' + modele + '}}'
else:
return '{{' + modele + '|ns=' + cas['ns'] + '}}'
else:
return match.group(0)
# Si un paramètre contient deux formes séparées par une virgule, on ne traite pas
if re.search(ur'\{\{de-tab-cas\s*\|[^}]+=[^=|}]*?,[^}]+?\}\}', contenu):
non_modifiee(titre, msg=u'Deux formes dans un paramètre')
return
if Test:
ldf = re.search(ur'(\'\'\'.*?\'\'\'.*?)\n\{\{de-tab-cas', contenu)
if ldf:
output('Ligne de forme : %s' % ldf.group(1))
# Le pluriel existe-t-il ? Si non, alors on pourra supprimer le pluriel de la ligne
# de forme lorsuqe redondant avec le tableau de flexions
# Cas 1 : absent car égal à -
no_plur = re.search(ur'\{\{de-tab-cas\s*\|[^}]+?\|\s*np *=\s*(?:\[\[[-–—]\]\]|[-–—]+)\s*\|', contenu)
# Cas 2 : absent car vide et (nominatif) singulier présent
if re.search(ur'\{\{de-tab-cas\s*\|\s*ns *=\s*(?:das|der|die) ', contenu) and \
re.search(ur'\{\{de-tab-cas\s*\|[^}]+?\|\s*np *=\s*\|', contenu):
no_plur = True
# de-tab-cas → de-nom
# contenu = re.sub(ur'\{\{de-tab-cas\s*\|', u'{{de-nom|', contenu)
contenu = re.sub(ur'\{\{de-tab-cas\s*\|\s*?ns *= *das (?:\'\'\'|\[\[)?([^\n|}]+?)(?:\'\'\'|\]\])?\n?\|np= *(?:die)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|as= *das (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|ap= *(?:die )?(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\s*\|gs= *des (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|gp= *(?:der)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\s*\|ds= *dem (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|dp= *(?:den)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[|)?([^][]*?)(?:\'\'\'|\]\])?\}\}',
ur'{{de-nom|genre=n\n|ns= \1 |np= \2\n|as= \3 |ap= \4\n|gs= \5 |gp= \6\n|ds= \7 |dp= \8}}', contenu)
contenu = re.sub(ur'\{\{de-tab-cas\s*\|\s*?ns= *die (?:\'\'\'|\[\[)?([^\n|}]*?)(?:\'\'\'|\]\])?\n?\|np= *(?:die)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\s*\|as= *die (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|ap= *(?:die)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\s*\|gs= *der (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|gp= *(?:der)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\s*\|ds= *der (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|dp= *(?:den)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\}\}',
ur'{{de-nom|genre=f\n|ns= \1 |np= \2\n|as= \3 |ap= \4\n|gs= \5 |gp= \6\n|ds= \7 |dp= \8}}', contenu)
contenu = re.sub(ur'\{\{de-tab-cas\s*\|\s*?ns= *der (?:\'\'\'|\[\[)?([^\n|}]+?)(?:\'\'\'|\]\])?\n?\|np= *(?:die)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\s*\|as= *den (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|ap= *(?:die)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\s*\|gs= *des (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|gp= *(?:der)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\s*\|ds= *dem (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|dp= *(?:den)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\s*\}\}',
ur'{{de-nom|genre=m\n|ns= \1 |np= \2\n|as= \3 |ap= \4\n|gs= \5 |gp= \6\n|ds= \7 |dp= \8}}', contenu)
# Idem, mais lorsque les paramètres pour le singulier sont tous placés au début
contenu = re.sub(ur'\{\{de-tab-cas\s*\|\s*?ns *= *das (?:\'\'\'|\[\[)?([^\n|}]+?)(?:\'\'\'|\]\])?\n?\|as= *das (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|gs= *des (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|ds= *dem (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|np= *(?:die)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|ap= *(?:die )?(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|gp= *(?:der)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|dp= *(?:den)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[|)?([^][]*?)(?:\'\'\'|\]\])?\}\}',
ur'{{de-nom|genre=n\n|ns= \1 |np= \5\n|as= \2 |ap= \6\n|gs= \3 |gp= \7\n|ds= \4 |dp= \8}}', contenu)
contenu = re.sub(ur'\{\{de-tab-cas\s*\|\s*?ns *= *die (?:\'\'\'|\[\[)?([^\n|}]+?)(?:\'\'\'|\]\])?\n?\|as= *die (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|gs= *der (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|ds= *der (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|np= *(?:die)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|ap= *(?:die )?(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|gp= *(?:der)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|dp= *(?:den)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[|)?([^][]*?)(?:\'\'\'|\]\])?\}\}',
ur'{{de-nom|genre=f\n|ns= \1 |np= \5\n|as= \2 |ap= \6\n|gs= \3 |gp= \7\n|ds= \4 |dp= \8}}', contenu)
contenu = re.sub(ur'\{\{de-tab-cas\s*\|\s*?ns *= *der (?:\'\'\'|\[\[)?([^\n|}]+?)(?:\'\'\'|\]\])?\n?\|as= *den (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|gs= *des (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|ds= *dem (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|np= *(?:die)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|ap= *(?:die )?(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|gp= *(?:der)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|dp= *(?:den)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[|)?([^][]*?)(?:\'\'\'|\]\])?\}\}',
ur'{{de-nom|genre=m\n|ns= \1 |np= \5\n|as= \2 |ap= \6\n|gs= \3 |gp= \7\n|ds= \4 |dp= \8}}', contenu)
# On enlève les tirets signalant l'absence de paramètre
R = re.compile(ur'(\{\{de-nom\|[^}]*?(?:np|ap|dp|gp|ns|as|ds|gs)) *= *[-–—]+(\}|\n|\|)')
oldContenu = contenu
contenu = R.sub(ur'\1=\2', contenu)
while oldContenu != contenu:
oldContenu = contenu
contenu = R.sub(ur'\1=\2', contenu)
contenu = re.sub(ur'\|(ns|as|gs|ds|np|ap|gp|dp)=\|', ur'|\1= |', contenu)
if re.search('\{\{de-nom\|[^}]*?(?:der|den|die|das) ', contenu):
non_modifiee(titre, msg=u'Article défini incorrect dans le tableau de flexions')
return
# de-nom → de-nom-xx
R = re.compile(ur'\{\{de-nom\|genre=(m|f|n)\n\|ns= ([^|}]*?) \|np= ([^|}]*?)\n\|as= ([^|}]*?) \|ap= ([^|}]*?)\n\|gs= ([^|}]*?) \|gp= ([^|}]*?)\n\|ds= ([^|}]*?) \|dp= ([^|}]*?)\}\}')
m = R.search(contenu)
# On extrait le nominatif pluriel dont on aura besoin plus base
nom_p = None
if m:
nom_p = m.group(3)
contenu = R.sub(deNomRepl, contenu)
# Placement du modèle avant la ligne du forme
contenu = re.sub(ur'\n(\'\'\'.*?\'\'\'.*?)\n(\{\{de-nom[^}]+?\}\})\n', ur'\n\2\n\1\n', contenu)
# Retrait du pluriel entre parenthèses sur la ligne de forme
# lorsque la pron est absente (ex : {{After]] : ' ({{p}}: '''die After''' /…/)')
if nom_p:
contenu = re.sub(ur'(\n\{\{de-nom[^}]+?\}\})\n(\'\'\'.*?\'\'\'.*?) *\(\{\{p\}\} *:? \'\'\'(?:die )?(?:\[\[)?%s(?:\]\])?\'\'\'(?: /[ …]?/| \{\{pron\|\|de\}\})? *\)\n' % nom_p,
ur'\1\n\2\n', contenu)
if no_plur:
contenu = re.sub(ur'(\n\{\{de-nom[^}]+?\}\})\n(\'\'\'.*?\'\'\'.*?) *\(\{\{p\}\} *:? \'\'\'[-–—]+\'\'\'(?: /[ …]?/| \{\{pron\|\|de\}\})? *\)\n',
ur'\1\n\2\n', contenu)
# Remplacement de // par {{pron||de}}
contenu = re.sub(ur'(\n\{\{de-nom[^}]+?\}\}\n\'\'\'[^\']*?\'\'\') /[ …]?/', ur'\1 {{pron||de}}', contenu)
# Ajout de {{pron}} sur la ligne de forme lorsque manquant
contenu = re.sub(ur'(\n\{\{de-nom[^}]+?\}\})\n(\'\'\'[^\']*?\'\'\') (\{\{[mfn]{1,2}\}\})\n', ur'\1\n\2 {{pron||de}} \3\n', contenu)
sauvegarde(page, contenu, summary)
def non_modifiee(titre, fichier=fichier_erreurs, msg=None):
'''
Ajout une page à un fichier d'erreurs avec un éventuel message d'erreur
'''
global editcounter
editcounter += 1
output(u'\n%s : %s\r\n' % (titre, msg))
pages_traitees.append(titre)
if edit_fileerror:
with codecs.open(fichier, 'a', config.textfile_encoding) as f:
if msg is None:
f.write(u'# [[%s]]\r\n' % titre)
else:
f.write(u'# [[%s]] : %s\r\n' % (titre, msg))
# adapté d'un script de JackPotte
def sauvegarde(PageCourante, Contenu, summary, log=True):
global pages_traitees
global pages_modifiees
global editcounter
titre = PageCourante.title()
modif = "o"
chgmt = Contenu != PageCourante.get() # Y a-t-il eu des changements ?
if not chgmt:
non_modifiee(titre, msg=u'Aucun changement')
return
while 1:
if Test:
showDiff(PageCourante.get(), Contenu)
modif = inputChoice(u"Sauvegarder ?",
["oui", "non", "ouvrir dans le navigateur", "quitter"],
["o", "n", "d", "q"], default="n")
if modif == "o":
if not Test:
ArretDUrgence()
try:
PageCourante.put(Contenu, summary)
except wikipedia.NoPage:
non_modifiee(titre, msg=u"NoPage en sauvegarde",)
return
except wikipedia.IsRedirectPage:
non_modifiee(titre, msg=u"IsRedirectPage en sauvegarde")
return
except wikipedia.LockedPage:
non_modifiee(titre, msg=u"LockedPage en sauvegarde")
return
except pywikibot.EditConflict:
non_modifiee(titre, msg=u"EditConflict en sauvegarde")
return
except wikipedia.ServerError:
non_modifiee(titre, msg=u"ServerError en sauvegarde")
return
except wikipedia.BadTitle:
non_modifiee(titre, msg=u"BadTitle en sauvegarde")
return
except AttributeError:
non_modifiee(titre, msg=u"AttributeError en sauvegarde")
return
else:
editcounter += 1
if log:
# Enregistrement de la liste des pages modifiées dans un fichier texte dédié
pages_traitees.append(titre)
pages_modifiees.append(titre)
if editcounter % frqMajListes == 0:
output(u'\03{lightgreen}%s pages modifiée(s)\03{default}' % editcounter)
with codecs.open(fichier_sortie, 'a', config.textfile_encoding) as f:
for page in pages_modifiees:
f.write(u"%s\r\n" % page)
retireFromList(pages_traitees)
pages_traitees = []
pages_modifiees = []
return
non_modifiee(titre, msg=u"Exception levée lors de la sauvegarde")
return
elif modif == 'd':
webbrowser.open("http://%s%s" % (
PageCourante.site.hostname(),
PageCourante.site.nice_get_address(titre)
))
i18n.input('pywikibot-enter-finished-browser')
continue
elif modif == 'q':
if len(pages_traitees) > 0:
with codecs.open(fichier_sortie, 'a', config.textfile_encoding) as f:
for page in pages_traitees:
f.write(u"%s\r\n" % page)
retireFromList(pages_traitees)
sys.exit()
else:
output(u"Non modifié (clic de l'utilisateur)")
return
# adapté d'un script de JackPotte
def ArretDUrgence():
page = Page(getSite(), u"User talk:Botomatik")
if page.exists():
try:
lastContributor = page.userName()
except wikipedia.NoPage: return
except wikipedia.IsRedirectPage: return
except wikipedia.LockedPage: return
except wikipedia.ServerError: return
except wikipedia.BadTitle: return
except wikipedia.EditConflict: return
if lastContributor != u'Automatik':
output(u"\n*** \03{lightyellow}Arrêt d'urgence demandé\03{default} ***")
sys.exit(0)
else:
output(u'La page de discussion du robot n\'existe pas')
def TraiteFichier(fichier=fichier_entree, fonction=main, *args, **kwargs):
"""
Applique une fonction à toutes les pages d'un fichier,
en lui passant la page pour premier paramètre
"""
gen = pagegenerators.TextfilePageGenerator(fichier)
for page in gen:
fonction(page, *args, **kwargs)
def retireFromList(pages):
"""Retire des pages du fichier d'entrée
Les pages doivent être sous la forme # [[page]]
@param pages: liste de pages
"""
with codecs.open(fichier_entree, 'r', config.textfile_encoding) as f:
contenu = f.read()
for page in pages:
contenu = re.sub(ur"# \[\[%s\]\]\r\n" % page, u"", contenu)
with codecs.open(fichier_entree, 'w', config.textfile_encoding) as f:
f.write(contenu)
if __name__ == '__main__':
try:
# main(Page(getSite(), 'Adamsapfel'))
TraiteFichier()
finally:
stopme()