def test_mako_exceptions_not_here(self): fLOG(__file__, self._testMethodName, OutputPrint=__name__ == "__main__") tmpl = """ % for i in range(0, len(lll)): print(${ll[i]}) % endfor """ exp = """ print(0) print(2) """ try: res = apply_template(tmpl, dict(lll=[0, 2])) assert False except CustomTemplateException as e: # fLOG(str(e)) assert "Undefined" in str(e) return assert False fLOG(res) self.assertEqual( res.replace(" ", "").replace("\n", ""), exp.replace(" ", "").replace("\n", ""))
def test_jinja2_exception_not_here(self): fLOG(__file__, self._testMethodName, OutputPrint=__name__ == "__main__") tmpl = """ {% for i in range(0, len(lll)) %} print({{ll[i]}}) {% endfor %} """ exp = """ print(0) print(2) """ try: res = apply_template(tmpl, dict(lll=[0, 2], len=len), engine="jinja2") assert False except CustomTemplateException as e: if "Some parameters are missing or mispelled" not in str(e): raise AssertionError(str(e)) from e return fLOG(res) self.assertEqual( res.replace(" ", "").replace("\n", ""), exp.replace(" ", "").replace("\n", ""))
def test_jinja2_exception(self): fLOG(__file__, self._testMethodName, OutputPrint=__name__ == "__main__") tmpl = """ {% for i in range(0, len(ll)) %} print({{ll[i]}}) {% end for %} """ exp = """ print(0) print(2) """ try: res = apply_template(tmpl, dict(ll=[0, 2], len=len), engine="jinja2") assert False except CustomTemplateException as e: assert "0005" in str(e) return assert False fLOG(res) self.assertEqual( res.replace(" ", "").replace("\n", ""), exp.replace(" ", "").replace("\n", ""))
def test_mako_exceptions_not_here(self): fLOG( __file__, self._testMethodName, OutputPrint=__name__ == "__main__") tmpl = """ % for i in range(0, len(lll)): print(${ll[i]}) % endfor """ exp = """ print(0) print(2) """ try: res = apply_template(tmpl, dict(lll=[0, 2])) assert False except CustomTemplateException as e: # fLOG(str(e)) assert "Undefined" in str(e) return assert False fLOG(res) self.assertEqual(res.replace(" ", "").replace("\n", ""), exp.replace(" ", "").replace("\n", ""))
def test_jinja2_exception_not_here(self): fLOG( __file__, self._testMethodName, OutputPrint=__name__ == "__main__") tmpl = """ {% for i in range(0, len(lll)) %} print({{ll[i]}}) {% endfor %} """ exp = """ print(0) print(2) """ try: res = apply_template(tmpl, dict( lll=[0, 2], len=len), engine="jinja2") assert False except CustomTemplateException as e: if "Some parameters are missing or mispelled" not in str(e): raise Exception(str(e)) return assert False fLOG(res) self.assertEqual(res.replace(" ", "").replace("\n", ""), exp.replace(" ", "").replace("\n", ""))
def test_jinja2(self): fLOG(__file__, self._testMethodName, OutputPrint=__name__ == "__main__") tmpl = """ {% for i in range(0, len(ll)) %} print({{ll[i]}}) {% endfor %} """ exp = """ print(0) print(2) """ res = apply_template(tmpl, dict(ll=[0, 2], len=len), engine="jinja2") fLOG(res) self.assertEqual( res.replace(" ", "").replace("\n", ""), exp.replace(" ", "").replace("\n", ""))
def test_mako(self): fLOG(__file__, self._testMethodName, OutputPrint=__name__ == "__main__") tmpl = """ % for i in range(0, len(ll)): print(${ll[i]}) % endfor """ exp = """ print(0) print(2) """ res = apply_template(tmpl, dict(ll=[0, 2])) fLOG(res) self.assertEqual( res.replace(" ", "").replace("\n", ""), exp.replace(" ", "").replace("\n", ""))
def test_jinja2(self): fLOG( __file__, self._testMethodName, OutputPrint=__name__ == "__main__") tmpl = """ {% for i in range(0, len(ll)) %} print({{ll[i]}}) {% endfor %} """ exp = """ print(0) print(2) """ res = apply_template(tmpl, dict(ll=[0, 2], len=len), engine="jinja2") fLOG(res) self.assertEqual(res.replace(" ", "").replace("\n", ""), exp.replace(" ", "").replace("\n", ""))
def test_mako(self): fLOG( __file__, self._testMethodName, OutputPrint=__name__ == "__main__") tmpl = """ % for i in range(0, len(ll)): print(${ll[i]}) % endfor """ exp = """ print(0) print(2) """ res = apply_template(tmpl, dict(ll=[0, 2])) fLOG(res) self.assertEqual(res.replace(" ", "").replace("\n", ""), exp.replace(" ", "").replace("\n", ""))
def enumerate_feedback(df1, col_group="Groupe", col_mail="Mail", col_name="Name", cols=["Sujet", "Rapport", "Code", "Soutenance"], subject=None, begin=None, end=None, template=template_mail_feedback, template_col=template_mail_columns, engine="jinja2", exc=True, fLOG=noLOG): """ Sends feedback to students. @param df1 dataframe @param col_group name of the column which contains the group definition @param col_mail name of the column which contains the mail of the members @param col_name name of the column which contains the names of the members @param cols list of columns to add to the mails, if there are multiple values per group, they will be joined by space or another separator if an element in this list is a tuple ``(col_name, sep)`` @param subject subject of the mail @param begin beginning of the mail @param end end of the mail (signature) @param template template of the mail @param template_col template for additional columns, the outcome will be joined to fill ``{{ content }}`` in the other template @param engine engine for the template @param exc raise an exception if there is no mail @return enumerate mails content as tuple *(mail, html, text)* Example of dataframe containing feedback: +------+-----------+--------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ | Mail | Name | Groupe | Sujet | Pitch | Code | +======+===========+========+=======================================+===================================================================================================================================================================================================================================================================+==================================================================================================================================================+ | | AAA bbb | 1 | | | | +------+-----------+--------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ | | ABA ccc | 1 | jeu de hex | ok | ok | +------+-----------+--------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ | | VVV uuu | 2 | | | | +------+-----------+--------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ | | ZZZZ xxxx | 2 | élections US, twitter, nuages de mots | ok | ok | +------+-----------+--------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ | | GGG ffff | 3 | distribution des sièges dans un avion | ok | Les print peuvent être remplacés par une autre fonction afin de désactiver les print qui ne servent qu'à la mise au point. | +------+-----------+--------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ | | ?? | 31 | | | | +------+-----------+--------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ | | RRRR yyyy | 31 | analyse de texte / nuage de mots | Il faut éviter le code dans le contenu du pitch. Le pitch est un peu flou quant aux raisons qui vous poussent à développer votre propre tokenizer. A bien justifier avant de vous lancer dans ce type de travail et ne pas oublier la question de son évaluation. | L'interface graphique est-elle indispensable ? Le code alterne fonction, lecture de texte. N'hésitez pas à séparer les deux pour le rendu final. | +------+-----------+--------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ """ if begin is None: raise ValueError("begin cannot be None, it should be string.") if end is None: raise ValueError("end cannot be None, it should be your signature.") if subject is None: raise ValueError( "subject cannot be None, it should be the subject of the mail.") def sums(spl): spl = [_ for _ in spl if isinstance(_, str) if "??" not in _] return ";".join(spl) def sums2(spl): spl = [_ for _ in spl if isinstance(_, str)] return ", ".join(spl) def sums3(spl, sep): res = [] for _ in spl: if isinstance(_, str): try: i = int(_) _ = i except ValueError: try: i = float(_) _ = i except ValueError: pass if isinstance(_, float): if numpy.isnan(_): continue elif int(_) == _: s_ = str(int(_)) else: s_ = str(_) else: s_ = str(_) if s_ not in res: res.append(s_) # pandas seems to change the type of the value # if extra characters are not added return sep.join(res) def clean_value(s): if isinstance(s, float): if numpy.isnan(s): return "" elif int(s) == s: return str(int(s)) else: return str(s) else: return str(s).strip() begin = begin.replace("\n", "<br />\n") end = end.replace("\n", "<br />\n") aggs = {col_mail: sums} if col_name is not None: aggs[col_name] = sums2 for c in cols: if isinstance(c, tuple): aggs[c[0]] = lambda s, sep=c[1]: sums3( # pylint: disable=W0631,W0640 s, sep) # pylint: disable=W0631,W0640 else: aggs[c] = lambda s: sums3(s, " ") if col_group is not None: group = df1.groupby(col_group).agg(aggs) common = dict(col_group=col_group, col_name=col_name, col_mail=col_mail) common_rev = {v: k for k, v in common.items()} lc = list(group.columns) colsi = [lc.index(c[0] if isinstance(c, tuple) else c) for c in cols] else: # already aggregated by group group = df1.groupby(col_mail).agg(aggs) common = dict(col_name=col_name, col_mail=col_mail) common_rev = {v: k for k, v in common.items()} lc = list(group.columns) colsi = [lc.index(c[0] if isinstance(c, tuple) else c) for c in cols] for row in group.itertuples(index=False): # key, value pairs content = [] for c, i in zip(cols, colsi): cn = c[0] if isinstance(c, tuple) else c v = clean_value(row[i]) if v: ct = dict(key=cn, value=v) text = apply_template(template_col, ct, engine=engine) content.append(text) # main mail context = common.copy() context["begin"] = begin context["end"] = end context["content"] = "\n".join(content) mail = None # rest of columns add to the context for k, v in zip(group.columns, row): if k == col_mail: mail = v k = common_rev.get(k, k) if k.startswith("col_"): k = k[4:] context[k] = clean_value(v) text = apply_template(template, context, engine=engine) if mail is None or "@" not in mail: if exc: raise ValueError("No mail for:\n" + text) else: fLOG("No mail for:\n" + text) html = ('<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head><body>\n' + text + "\n</body></html>\n") text = text.replace("<b>", "").replace( "</b>", "").replace("<br />", "\n") yield (mail, html, text)
def enumerate_feedback(df1, col_group="Groupe", col_mail="Mail", col_name="Name", cols=["Sujet", "Rapport", "Code", "Soutenance"], subject=None, begin=None, end=None, template=template_mail_feedback, template_col=template_mail_columns, engine="jinja2", exc=True, fLOG=noLOG): """ sends feedback to students @param df1 dataframe @param col_group name of the column which contains the group definition @param col_mail name of the column which contains the mail of the members @param col_name name of the column which contains the names of the members @param cols list of columns to add to the mails, if there are multiple values per group, they will be joined by space or another separator if an element in this list is a tuple ``(col_name, sep)`` @param subject subject of the mail @param begin beginning of the mail @param template template of the mail @param template_col template for additional columns, the outcome will be joined to fill ``{{ content }}`` in the other template @param engine engine for the template @param exc raise an exception if there is no mail @return enumerate mails content as tuple *(mail, html, text)* Example of dataframe containing feedback: +------+-----------+--------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ | Mail | Name | Groupe | Sujet | Pitch | Code | +======+===========+========+=======================================+===================================================================================================================================================================================================================================================================+==================================================================================================================================================+ | | AAA bbb | 1 | | | | +------+-----------+--------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ | | ABA ccc | 1 | jeu de hex | ok | ok | +------+-----------+--------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ | | VVV uuu | 2 | | | | +------+-----------+--------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ | | ZZZZ xxxx | 2 | élections US, twitter, nuages de mots | ok | ok | +------+-----------+--------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ | | GGG ffff | 3 | distribution des sièges dans un avion | ok | Les print peuvent être remplacés par une autre fonction afin de désactiver les print qui ne servent qu'à la mise au point. | +------+-----------+--------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ | | ?? | 31 | | | | +------+-----------+--------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ | | RRRR yyyy | 31 | analyse de texte / nuage de mots | Il faut éviter le code dans le contenu du pitch. Le pitch est un peu flou quant aux raisons qui vous poussent à développer votre propre tokenizer. A bien justifier avant de vous lancer dans ce type de travail et ne pas oublier la question de son évaluation. | L'interface graphique est-elle indispensable ? Le code alterne fonction, lecture de texte. N'hésitez pas à séparer les deux pour le rendu final. | +------+-----------+--------+---------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ """ if begin is None: raise ValueError("begin cannot be None, it should be string.") if end is None: raise ValueError("end cannot be None, it should be you signature.") if subject is None: raise ValueError( "subject cannot be None, it should be the subject of the mail.") def sums(spl): spl = [_ for _ in spl if isinstance(_, str) if "??" not in _] return ";".join(spl) def sums2(spl): spl = [_ for _ in spl if isinstance(_, str)] return ", ".join(spl) def sums3(spl, sep): res = [] for _ in spl: if isinstance(_, str): try: i = int(_) _ = i except ValueError: try: i = float(_) _ = i except ValueError: pass if isinstance(_, float): if numpy.isnan(_): continue elif int(_) == _: s_ = str(int(_)) else: s_ = str(_) else: s_ = str(_) if s_ not in res: res.append(s_) # pandas seems to change the type of the value # if extra characters are not added return sep.join(res) def clean_value(s): if isinstance(s, float): if numpy.isnan(s): return "" elif int(s) == s: return str(int(s)) else: return str(s) else: return str(s).strip() begin = begin.replace("\n", "<br />\n") end = end.replace("\n", "<br />\n") aggs = {col_mail: sums, col_name: sums2} for c in cols: if isinstance(c, tuple): aggs[c[0]] = lambda s, sep=c[1]: sums3(s, sep) else: aggs[c] = lambda s: sums3(s, " ") if col_group is not None: group = df1.groupby(col_group).agg(aggs) common = dict(col_group=col_group, col_name=col_name, col_mail=col_mail) common_rev = {v: k for k, v in common.items()} lc = list(group.columns) colsi = [lc.index(c[0] if isinstance(c, tuple) else c) for c in cols] else: # already aggregated by group group = df1.groupby(col_mail).agg(aggs) common = dict(col_name=col_name, col_mail=col_mail) common_rev = {v: k for k, v in common.items()} lc = list(group.columns) colsi = [lc.index(c[0] if isinstance(c, tuple) else c) for c in cols] for row in group.itertuples(index=False): # key, value pairs content = [] for c, i in zip(cols, colsi): cn = c[0] if isinstance(c, tuple) else c v = clean_value(row[i]) if v: ct = dict(key=cn, value=v) text = apply_template(template_col, ct, engine=engine) content.append(text) # main mail context = common.copy() context["begin"] = begin context["end"] = end context["content"] = "\n".join(content) mail = None # rest of columns add to the context for k, v in zip(group.columns, row): if k == col_mail: mail = v k = common_rev.get(k, k) if k.startswith("col_"): k = k[4:] context[k] = clean_value(v) text = apply_template(template, context, engine=engine) if mail is None or "@" not in mail: if exc: raise ValueError("No mail for:\n" + text) else: fLOG("No mail for:\n" + text) html = ( '<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head><body>\n' + text + "\n</body></html>\n") text = text.replace("<b>", "").replace("</b>", "").replace("<br />", "\n") yield (mail, html, text)