From 4f65b753e6bddcbcb6c73793e75fd2bfe8195114 Mon Sep 17 00:00:00 2001 From: Ping-uu Date: Sat, 9 Jan 2021 21:50:28 +0100 Subject: [PATCH 1/4] Added Support for: - PDF - Enhanced XMIND 8 - Refactoring some report functions --- maigret/maigret.py | 38 ++++--- maigret/report.py | 145 +++++++++++++++++------- maigret/resources/simple_report_pdf.css | 0 maigret/resources/simple_report_pdf.tpl | 106 +++++++++++++++++ requirements.txt | 26 ++++- 5 files changed, 262 insertions(+), 53 deletions(-) create mode 100644 maigret/resources/simple_report_pdf.css create mode 100644 maigret/resources/simple_report_pdf.tpl diff --git a/maigret/maigret.py b/maigret/maigret.py index 20d6bcc..5c64abc 100755 --- a/maigret/maigret.py +++ b/maigret/maigret.py @@ -26,7 +26,7 @@ from socid_extractor import parse, extract from .notify import QueryNotifyPrint from .result import QueryResult, QueryStatus from .sites import MaigretDatabase, MaigretSite -from .report import save_csv_report, genxmindfile, save_html_report +from .report import save_csv_report, genxmindfile, save_html_pdf_report import xmind @@ -285,6 +285,8 @@ def process_site_result(response, query_notify, logger, results_info, site: Maig return results_info + + async def maigret(username, site_dict, query_notify, logger, proxy=None, timeout=None, recursive_search=False, id_type='username', tags=None, debug=False, forced=False, @@ -705,6 +707,12 @@ async def main(): help="Generate an xmind 8 mindmap" ) + parser.add_argument("-P", "--pdf", + action="store_true", + dest="pdf", default=False, + help="Generate a pdf report" + ) + args = parser.parse_args() # Logging @@ -851,18 +859,15 @@ async def main(): # The usernames results should be stored in a targeted folder. # If the folder doesn't exist, create it first os.makedirs(args.folderoutput, exist_ok=True) - result_file = os.path.join(args.folderoutput, f"{username}.txt") - if args.xmind: - xmind_path = os.path.join(args.folderoutput, f"{username}.xmind") + result_path = os.path.join(args.folderoutput, f"{username}.") else: - result_file = f"{username}.txt" - if args.xmind: - xmind_path = f"{username}.xmind" + result_path = os.path.join("reports", f"{username}.") if args.xmind: - genxmindfile(xmind_path, username, results) + genxmindfile(result_path+"xmind", username, results) - with open(result_file, "w", encoding="utf-8") as file: + + with open(result_path+"txt", "w", encoding="utf-8") as file: exists_counter = 0 for website_name in results: dictionary = results[website_name] @@ -878,12 +883,20 @@ async def main(): exists_counter += 1 file.write(dictionary["url_user"] + "\n") file.write(f"Total Websites Username Detected On : {exists_counter}") + file.close() if args.csv: - save_csv_report(username, results) + save_csv_report(username, results, result_path+"csv") - if args.html: - save_html_report(general_results) + pathPDF = None + pathHTML = None + if args.html: + pathHTML = result_path+"html" + if args.pdf: + pathPDF = result_path+"pdf" + + if pathPDF or pathHTML: + save_html_pdf_report(general_results,pathHTML,pathPDF) def run(): @@ -894,6 +907,5 @@ def run(): print('Maigret is interrupted.') sys.exit(1) - if __name__ == "__main__": run() \ No newline at end of file diff --git a/maigret/report.py b/maigret/report.py index 58c697b..75e7769 100644 --- a/maigret/report.py +++ b/maigret/report.py @@ -5,26 +5,58 @@ import os import xmind from jinja2 import Template +from weasyprint import HTML, CSS + import pycountry from .result import QueryStatus from .utils import is_country_tag, CaseConverter, enrich_link_str - -def save_csv_report(username: str, results: dict): - with open(username + '.csv', 'w', newline='', encoding='utf-8') as csvfile: +def save_csv_report(username: str, results: dict, filename:str): + with open(filename, 'w', newline='', encoding='utf-8') as csvfile: save_csv_report_to_file(username, results, csvfile) +def retrive_timestamp(datestring:str): + first_seen_format = '%Y-%m-%d %H:%M:%S' + first_seen_formats = '%Y-%m-%dT%H:%M:%S' + try: + time = datetime.strptime(datestring, first_seen_format) + except: + try: + time = datetime.strptime(datestring, first_seen_formats) + except: + time = datetime.min + return time -def save_html_report(username_results: list): +def filterSupposedData(data): + ### interesting fields + allowed_fields = ['fullname', 'gender', 'location'] + filtered_supposed_data = {CaseConverter.snake_to_title(k): v[0] + for k, v in data.items() + if k in allowed_fields} + return filtered_supposed_data + +def generate_template(pdf:bool): + # template generation + if(pdf): + template_text = open(os.path.join(os.path.dirname(os.path.realpath(__file__)), + "resources/simple_report_pdf.tpl")).read() + else: + template_text = open(os.path.join(os.path.dirname(os.path.realpath(__file__)), + "resources/simple_report.tpl")).read() + template = Template(template_text) + template.globals['title'] = CaseConverter.snake_to_title + template.globals['detect_link'] = enrich_link_str + return template + +def save_html_pdf_report(username_results: list, filename:str=None, filenamepdf:str=None): brief_text = [] usernames = {} extended_info_count = 0 tags = {} supposed_data = {} - allowed_fields = ['fullname', 'gender'] + first_seen = None - first_seen_format = '%Y-%m-%d %H:%M:%S' for username, id_type, results in username_results: found_accounts = 0 @@ -51,8 +83,8 @@ def save_html_report(username_results: list): if first_seen is None: first_seen = created_at else: - known_time = datetime.strptime(first_seen, first_seen_format) - new_time = datetime.strptime(created_at, first_seen_format) + known_time = retrive_timestamp(first_seen) + new_time = retrive_timestamp(created_at) if new_time < known_time: first_seen = created_at @@ -103,13 +135,7 @@ def save_html_report(username_results: list): brief_text.append(f'Extended info extracted from {extended_info_count} accounts.') - # template generation - template_text = open(os.path.join(os.path.dirname(os.path.realpath(__file__)), - "resources/simple_report.tpl")).read() - template = Template(template_text) - template.globals['title'] = CaseConverter.snake_to_title - template.globals['detect_link'] = enrich_link_str brief = ' '.join(brief_text).strip() tuple_sort = lambda d: sorted(d, key=lambda x: x[1], reverse=True) @@ -122,23 +148,37 @@ def save_html_report(username_results: list): countries_lists = list(filter(lambda x: is_country_tag(x[0]), tags.items())) interests_list = list(filter(lambda x: not is_country_tag(x[0]), tags.items())) - filtered_supposed_data = {CaseConverter.snake_to_title(k): v[0] - for k, v in supposed_data.items() - if k in allowed_fields} + filtered_supposed_data = filterSupposedData(supposed_data) - filled_template = template.render(username=first_username, - brief=brief, - results=username_results, - first_seen=first_seen, - interests_tuple_list=tuple_sort(interests_list), - countries_tuple_list=tuple_sort(countries_lists), - supposed_data=filtered_supposed_data, - generated_at=datetime.now().strftime('%Y-%m-%d %H:%M:%S'), - ) - # save report - html_filename = f'report_{first_username}.html' - with open(html_filename, 'w') as f: - f.write(filled_template) + # save report in HTML + if(filename is not None): + template = generate_template(False) + filled_template = template.render(username=first_username, + brief=brief, + results=username_results, + first_seen=first_seen, + interests_tuple_list=tuple_sort(interests_list), + countries_tuple_list=tuple_sort(countries_lists), + supposed_data=filtered_supposed_data, + generated_at=datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + ) + with open(filename, 'w') as f: + f.write(filled_template) + f.close() + # save report in PDF + if(filenamepdf is not None): + template = generate_template(True) + filled_template = template.render(username=first_username, + brief=brief, + results=username_results, + first_seen=first_seen, + interests_tuple_list=tuple_sort(interests_list), + countries_tuple_list=tuple_sort(countries_lists), + supposed_data=filtered_supposed_data, + generated_at=datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + ) + HTML(string=filled_template).write_pdf(filenamepdf, stylesheets=[CSS(filename=os.path.join(os.path.dirname(os.path.realpath(__file__)), + "resources/simple_report_pdf.css"))]) def save_csv_report_to_file(username: str, results: dict, csvfile): print(results) @@ -160,20 +200,23 @@ def save_csv_report_to_file(username: str, results: dict, csvfile): results[site]['http_status'], ]) - +''' +XMIND 8 Functions +''' def genxmindfile(filename, username, results): print(f'Generating XMIND8 file for username {username}') if os.path.exists(filename): os.remove(filename) workbook = xmind.load(filename) sheet = workbook.getPrimarySheet() - design_sheet1(sheet, username, results) + design_sheet(sheet, username, results) xmind.save(workbook, path=filename) -def design_sheet1(sheet, username, results): +def design_sheet(sheet, username, results): ##all tag list alltags = {} + supposed_data = {} sheet.setTitle("%s Analysis"%(username)) root_topic1 = sheet.getRootTopic() @@ -198,7 +241,6 @@ def design_sheet1(sheet, username, results): alltags[tag] = tagsection category = None - userlink= None for tag in dictionary.get("status").tags: if tag.strip() == "": continue @@ -206,12 +248,37 @@ def design_sheet1(sheet, username, results): category = tag if category is None: - category = "undefined" userlink = undefinedsection.addSubTopic() + userlink.addLabel(dictionary.get("status").site_url_user) else: userlink = alltags[category].addSubTopic() - userlink.addLabel(dictionary.get("status").site_url_user) + userlink.addLabel(dictionary.get("status").site_url_user) + + if dictionary.get("status").ids_data: + for k, v in dictionary.get("status").ids_data.items(): + # suppose target data + if not isinstance(v, list): + currentsublabel = userlink.addSubTopic() + field = 'fullname' if k == 'name' else k + if not field in supposed_data: + supposed_data[field] = [] + supposed_data[field].append(v) + currentsublabel.setTitle("%s: %s" % (k, v)) + else: + for currentval in v: + currentsublabel = userlink.addSubTopic() + field = 'fullname' if k == 'name' else k + if not field in supposed_data: + supposed_data[field] = [] + supposed_data[field].append(currentval) + currentsublabel.setTitle("%s: %s" % (k, currentval)) + ### Add Supposed DATA + filterede_supposed_data = filterSupposedData(supposed_data) + if(len(filterede_supposed_data) >0): + undefinedsection = root_topic1.addSubTopic() + undefinedsection.setTitle("SUPPOSED DATA") + for k, v in filterede_supposed_data.items(): + currentsublabel = undefinedsection.addSubTopic() + currentsublabel.setTitle("%s: %s" % (k, v)) + - #for tag in dictionary.get("status").tags: - # if( tag != category ): - # sheet.createRelationship(userlink.getID(), alltags[tag].getID(),"other tag") diff --git a/maigret/resources/simple_report_pdf.css b/maigret/resources/simple_report_pdf.css new file mode 100644 index 0000000..e69de29 diff --git a/maigret/resources/simple_report_pdf.tpl b/maigret/resources/simple_report_pdf.tpl new file mode 100644 index 0000000..3e0a0a0 --- /dev/null +++ b/maigret/resources/simple_report_pdf.tpl @@ -0,0 +1,106 @@ + + + + + +{{ username }} -- Maigret username search report + + +
+
+
+

+ Username search report for {{ username }} +

+ Generated at {{ generated_at }} +
+
+
+
+
+
+
Supposed personal data
+ {% for k, v in supposed_data.items() %} + + {{ k }}: {{ v }} + + {% endfor %} + {% if countries_tuple_list %} + + Geo: {% for k, v in countries_tuple_list %}{{ k }} ({{ v }}){{ ", " if not loop.last }}{% endfor %} + + {% endif %}{% if interests_tuple_list %} + + Interests: {% for k, v in interests_tuple_list %}{{ k }} ({{ v }}){{ ", " if not loop.last }}{% endfor %} + + {% endif %}{% if first_seen %} + + First seen: {{ first_seen }} + + {% endif %} +
+
+
+
+
+
+
+
+
Brief
+ + {{ brief }} + +
+
+
+
+ {% for u, t, data in results %} + {% for k, v in data.items() %} + {% if v.found and not v.is_similar %} +
+
+
+ Photo +
+

+ {{ k }} +

+ {% if v.status.tags %} +
Tags: {{ v.status.tags | join(', ') }}
+ {% endif %} +

+ {{ v.url_user }} +

+ {% if v.ids_data %} + + + {% for k1, v1 in v.ids_data.items() %} + {% if k1 != 'image' %} + + + + + {% endif %} + {% endfor %} + +
{{ title(k1) }}{% if v1 is iterable and (v1 is not string and v1 is not mapping) %}{{ v1 | join(', ') }}{% else %}{{ detect_link(v1) }}{% endif %} +
+ {% endif %} +

+
+
+
+
+ {% endif %} + {% endfor %} + {% endfor %} +
+ + \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 060f64b..6bf22a8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,27 +2,51 @@ aiohttp==3.7.3 aiohttp-socks==0.5.5 async-timeout==3.0.1 attrs==20.3.0 +backports.functools-lru-cache==1.6.1 beautifulsoup4==4.9.3 bs4==0.0.1 +cairocffi==1.2.0 +CairoSVG==2.5.1 certifi==2020.12.5 +cffi==1.14.4 chardet==3.0.4 colorama==0.4.4 +cssselect2==0.4.1 +defusedxml==0.6.0 +html5lib==1.1 idna==2.10 +importlib-metadata==3.3.0 Jinja2==2.11.2 lxml==4.6.2 +Markdown==3.3.3 +MarkupSafe==1.1.1 mock==4.0.2 multidict==5.1.0 +numpy==1.19.5 +pandas==1.2.0 +pdf-reports==0.3.2 +Pillow==8.1.0 pycountry==20.7.3 +pycparser==2.20 +Pyphen==0.10.0 +pypugjs==5.9.8 PySocks==1.7.1 +python-dateutil==2.8.1 python-socks==1.1.2 +pytz==2020.5 requests==2.25.1 requests-futures==1.0.0 +six==1.15.0 socid-extractor==0.0.2 soupsieve==2.1 stem==1.8.0 +tinycss2==1.1.0 torrequest==0.1.0 -tqdm==4.55.0 +tqdm==4.55.1 typing-extensions==3.7.4.3 urllib3==1.26.2 +WeasyPrint==52.2 +webencodings==0.5.1 XMind==1.2.0 yarl==1.6.3 +zipp==3.4.0 From e5b35125085b0fc3c121439b43dcc0681831aae7 Mon Sep 17 00:00:00 2001 From: Ping-uu Date: Sat, 9 Jan 2021 21:57:40 +0100 Subject: [PATCH 2/4] "CSS and TPL fixes" --- maigret/resources/simple_report_pdf.css | 123 ++++++++++++++++++++++++ maigret/resources/simple_report_pdf.tpl | 30 +++--- 2 files changed, 140 insertions(+), 13 deletions(-) diff --git a/maigret/resources/simple_report_pdf.css b/maigret/resources/simple_report_pdf.css index e69de29..291df93 100644 --- a/maigret/resources/simple_report_pdf.css +++ b/maigret/resources/simple_report_pdf.css @@ -0,0 +1,123 @@ +/* -------------------------------------------------------------- + + typography.css + * Sets up some sensible default typography. + +-------------------------------------------------------------- */ + +/* Default font settings. + The font-size percentage is of 16px. (0.75 * 16px = 12px) */ +html { font-size:100.01%; } +body { + font-size: 75%; + color: #222; + background: #fff; + font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; +} + + +/* Headings +-------------------------------------------------------------- */ + +h1,h2,h3,h4,h5,h6 { font-weight: normal; color: #111; } + +h1 { font-size: 3em; line-height: 1; margin-bottom: 0.5em; } +h2 { font-size: 2em; margin-bottom: 0.75em; } +h3 { font-size: 1.5em; line-height: 1; margin-bottom: 1em; } +h4 { font-size: 1.2em; line-height: 1.25; margin-bottom: 1.25em; } +h5 { font-size: 1em; font-weight: bold; margin-bottom: 1.5em; } +h6 { font-size: 1em; font-weight: bold; } + +h1 img, h2 img, h3 img, +h4 img, h5 img, h6 img { + margin: 0; +} + + +/* Text elements +-------------------------------------------------------------- */ + +p { margin: 0 0 1.5em; } +/* + These can be used to pull an image at the start of a paragraph, so + that the text flows around it (usage:

Text

) + */ +.left { float: left !important; } +p .left { margin: 1.5em 1.5em 1.5em 0; padding: 0; } +.right { float: right !important; } +p .right { margin: 1.5em 0 1.5em 1.5em; padding: 0; } + +a:focus, +a:hover { color: #09f; } +a { color: #06c; text-decoration: underline; } + +blockquote { margin: 1.5em; color: #666; font-style: italic; } +strong,dfn { font-weight: bold; } +em,dfn { font-style: italic; } +sup, sub { line-height: 0; } + +abbr, +acronym { border-bottom: 1px dotted #666; } +address { margin: 0 0 1.5em; font-style: italic; } +del { color:#666; } + +pre { margin: 1.5em 0; white-space: pre; } +pre,code,tt { font: 1em 'andale mono', 'lucida console', monospace; line-height: 1.5; } + + +/* Lists +-------------------------------------------------------------- */ + +li ul, +li ol { margin: 0; } +ul, ol { margin: 0 1.5em 1.5em 0; padding-left: 1.5em; } + +ul { list-style-type: disc; } +ol { list-style-type: decimal; } + +dl { margin: 0 0 1.5em 0; } +dl dt { font-weight: bold; } +dd { margin-left: 1.5em;} + + +/* Tables +-------------------------------------------------------------- */ + +/* + Because of the need for padding on TH and TD, the vertical rhythm + on table cells has to be 27px, instead of the standard 18px or 36px + of other elements. + */ +table { margin-bottom: 1.4em; width:100%; } +th { font-weight: bold; } +thead th { background: #c3d9ff; } +th,td,caption { padding: 4px 10px 4px 5px; } +/* + You can zebra-stripe your tables in outdated browsers by adding + the class "even" to every other table row. + */ +tbody tr:nth-child(even) td, +tbody tr.even td { + background: #e5ecf9; +} +tfoot { font-style: italic; } +caption { background: #eee; } + + +/* Misc classes +-------------------------------------------------------------- */ + +.small { font-size: .8em; margin-bottom: 1.875em; line-height: 1.875em; } +.large { font-size: 1.2em; line-height: 2.5em; margin-bottom: 1.25em; } +.hide { display: none; } + +.quiet { color: #b7b7b7; } +.loud { color: #000; } +.highlight { background:#ff0; } +.added { background:#060; color: #fff; } +.removed { background:#900; color: #fff; } + +.first { margin-left:0; padding-left:0; } +.last { margin-right:0; padding-right:0; } +.top { margin-top:0; padding-top:0; } +.bottom { margin-bottom:0; padding-bottom:0; } \ No newline at end of file diff --git a/maigret/resources/simple_report_pdf.tpl b/maigret/resources/simple_report_pdf.tpl index 3e0a0a0..ae57e29 100644 --- a/maigret/resources/simple_report_pdf.tpl +++ b/maigret/resources/simple_report_pdf.tpl @@ -16,9 +16,9 @@
-

+

Username search report for {{ username }} -

+ Generated at {{ generated_at }}
@@ -26,7 +26,7 @@
-
Supposed personal data
+

Supposed personal data

{% for k, v in supposed_data.items() %} {{ k }}: {{ v }} @@ -53,7 +53,7 @@
-
Brief
+

Brief

{{ brief }} @@ -64,11 +64,12 @@ {% for u, t, data in results %} {% for k, v in data.items() %} {% if v.found and not v.is_similar %} -
+
+
- Photo -
+ Photo +

{{ k }}

@@ -79,22 +80,25 @@ {{ v.url_user }}

{% if v.ids_data %} - + +
+
+

Details

+
{% for k1, v1 in v.ids_data.items() %} {% if k1 != 'image' %} - - + + {% endif %} + {% endfor %}
{{ title(k1) }}{% if v1 is iterable and (v1 is not string and v1 is not mapping) %}{{ v1 | join(', ') }}{% else %}{{ detect_link(v1) }}{% endif %} - {{ title(k1) }}{% if v1 is iterable and (v1 is not string and v1 is not mapping) %}{{ v1 | join(', ') }}{% else %}{{ detect_link(v1) }}{% endif %}
+
{% endif %} -

-
From 9a51fc37c875afbe9ebf58e0242ffb0f51c9b2cd Mon Sep 17 00:00:00 2001 From: Ping-uu Date: Mon, 11 Jan 2021 00:53:09 +0100 Subject: [PATCH 3/4] Pdf engine rewrited using xhtml2pdf --- .gitignore | 1 + maigret/report.py | 19 ++- maigret/resources/simple_report_pdf.css | 154 ++++++------------------ maigret/resources/simple_report_pdf.tpl | 91 +++++++------- requirements.txt | 27 ++--- 5 files changed, 107 insertions(+), 185 deletions(-) diff --git a/.gitignore b/.gitignore index 3bf7f81..919b269 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ tests/.excluded_sites # MacOS Folder Metadata File .DS_Store +/reports/ diff --git a/maigret/report.py b/maigret/report.py index 75e7769..6211995 100644 --- a/maigret/report.py +++ b/maigret/report.py @@ -3,9 +3,10 @@ from datetime import datetime import logging import os import xmind +import io +from xhtml2pdf import pisa from jinja2 import Template -from weasyprint import HTML, CSS import pycountry @@ -177,8 +178,20 @@ def save_html_pdf_report(username_results: list, filename:str=None, filenamepdf: supposed_data=filtered_supposed_data, generated_at=datetime.now().strftime('%Y-%m-%d %H:%M:%S'), ) - HTML(string=filled_template).write_pdf(filenamepdf, stylesheets=[CSS(filename=os.path.join(os.path.dirname(os.path.realpath(__file__)), - "resources/simple_report_pdf.css"))]) + csstext = "" + with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), + "resources/simple_report_pdf.css"), "r") as cssfile: + cssline = cssfile.readline() + csstext += cssline + while cssline: + cssline = cssfile.readline() + csstext += cssline + cssfile.close() + + pdffile = open(filenamepdf, "w+b") + pisa.pisaDocument(io.StringIO(filled_template), dest=pdffile, default_css=csstext) + pdffile.close() + def save_csv_report_to_file(username: str, results: dict, csvfile): print(results) diff --git a/maigret/resources/simple_report_pdf.css b/maigret/resources/simple_report_pdf.css index 291df93..9fda1b4 100644 --- a/maigret/resources/simple_report_pdf.css +++ b/maigret/resources/simple_report_pdf.css @@ -1,123 +1,41 @@ -/* -------------------------------------------------------------- - - typography.css - * Sets up some sensible default typography. - --------------------------------------------------------------- */ - -/* Default font settings. - The font-size percentage is of 16px. (0.75 * 16px = 12px) */ -html { font-size:100.01%; } -body { - font-size: 75%; - color: #222; - background: #fff; - font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; +h2 { + font-size: 30px; + width: 100%; + display:block; +} +h3 { + font-size: 25px; + width: 100%; + display:block; +} +h4 { + font-size: 20px; + width: 100%; + display:block; +} +p { + margin: 0 0 5px; + display: block; } -/* Headings --------------------------------------------------------------- */ - -h1,h2,h3,h4,h5,h6 { font-weight: normal; color: #111; } - -h1 { font-size: 3em; line-height: 1; margin-bottom: 0.5em; } -h2 { font-size: 2em; margin-bottom: 0.75em; } -h3 { font-size: 1.5em; line-height: 1; margin-bottom: 1em; } -h4 { font-size: 1.2em; line-height: 1.25; margin-bottom: 1.25em; } -h5 { font-size: 1em; font-weight: bold; margin-bottom: 1.5em; } -h6 { font-size: 1em; font-weight: bold; } - -h1 img, h2 img, h3 img, -h4 img, h5 img, h6 img { - margin: 0; +table { + margin-bottom: 10px; + width:100%; +} +th { + font-weight: bold; +} +th,td,caption { + padding: 4px 10px 4px 5px; +} +table tr:nth-child(even) td, +table tr.even td { + background-color: #e5ecf9; } - -/* Text elements --------------------------------------------------------------- */ - -p { margin: 0 0 1.5em; } -/* - These can be used to pull an image at the start of a paragraph, so - that the text flows around it (usage:

Text

) - */ -.left { float: left !important; } -p .left { margin: 1.5em 1.5em 1.5em 0; padding: 0; } -.right { float: right !important; } -p .right { margin: 1.5em 0 1.5em 1.5em; padding: 0; } - -a:focus, -a:hover { color: #09f; } -a { color: #06c; text-decoration: underline; } - -blockquote { margin: 1.5em; color: #666; font-style: italic; } -strong,dfn { font-weight: bold; } -em,dfn { font-style: italic; } -sup, sub { line-height: 0; } - -abbr, -acronym { border-bottom: 1px dotted #666; } -address { margin: 0 0 1.5em; font-style: italic; } -del { color:#666; } - -pre { margin: 1.5em 0; white-space: pre; } -pre,code,tt { font: 1em 'andale mono', 'lucida console', monospace; line-height: 1.5; } - - -/* Lists --------------------------------------------------------------- */ - -li ul, -li ol { margin: 0; } -ul, ol { margin: 0 1.5em 1.5em 0; padding-left: 1.5em; } - -ul { list-style-type: disc; } -ol { list-style-type: decimal; } - -dl { margin: 0 0 1.5em 0; } -dl dt { font-weight: bold; } -dd { margin-left: 1.5em;} - - -/* Tables --------------------------------------------------------------- */ - -/* - Because of the need for padding on TH and TD, the vertical rhythm - on table cells has to be 27px, instead of the standard 18px or 36px - of other elements. - */ -table { margin-bottom: 1.4em; width:100%; } -th { font-weight: bold; } -thead th { background: #c3d9ff; } -th,td,caption { padding: 4px 10px 4px 5px; } -/* - You can zebra-stripe your tables in outdated browsers by adding - the class "even" to every other table row. - */ -tbody tr:nth-child(even) td, -tbody tr.even td { - background: #e5ecf9; -} -tfoot { font-style: italic; } -caption { background: #eee; } - - -/* Misc classes --------------------------------------------------------------- */ - -.small { font-size: .8em; margin-bottom: 1.875em; line-height: 1.875em; } -.large { font-size: 1.2em; line-height: 2.5em; margin-bottom: 1.25em; } -.hide { display: none; } - -.quiet { color: #b7b7b7; } -.loud { color: #000; } -.highlight { background:#ff0; } -.added { background:#060; color: #fff; } -.removed { background:#900; color: #fff; } - -.first { margin-left:0; padding-left:0; } -.last { margin-right:0; padding-right:0; } -.top { margin-top:0; padding-top:0; } -.bottom { margin-bottom:0; padding-bottom:0; } \ No newline at end of file +div { + border-bottom-color: #3e3e3e; + border-bottom-width: 1px; + border-bottom-style: solid; +} \ No newline at end of file diff --git a/maigret/resources/simple_report_pdf.tpl b/maigret/resources/simple_report_pdf.tpl index ae57e29..aa58ae5 100644 --- a/maigret/resources/simple_report_pdf.tpl +++ b/maigret/resources/simple_report_pdf.tpl @@ -1,17 +1,9 @@ - +type="text/css" {{ username }} -- Maigret username search report -
@@ -22,41 +14,41 @@ Generated at {{ generated_at }}
-
-
-
-
+
+
+
+

Supposed personal data

{% for k, v in supposed_data.items() %} - +

{{ k }}: {{ v }} - +

{% endfor %} {% if countries_tuple_list %} - +

Geo: {% for k, v in countries_tuple_list %}{{ k }} ({{ v }}){{ ", " if not loop.last }}{% endfor %} - +

{% endif %}{% if interests_tuple_list %} - +

Interests: {% for k, v in interests_tuple_list %}{{ k }} ({{ v }}){{ ", " if not loop.last }}{% endfor %} - +

{% endif %}{% if first_seen %} - +

First seen: {{ first_seen }} - +

{% endif %}
-
-
-
-
+
+
+
+

Brief

- +

{{ brief }} - +

@@ -64,26 +56,37 @@ {% for u, t, data in results %} {% for k, v in data.items() %} {% if v.found and not v.is_similar %} -
-
-
-
- Photo -
-

- {{ k }} -

- {% if v.status.tags %} -
Tags: {{ v.status.tags | join(', ') }}
- {% endif %} -

- {{ v.url_user }} -

+ +
+
+
+
+ + + + + + +
+ Photo + +
+

+ {{ k }} +

+ {% if v.status.tags %} +
Tags: {{ v.status.tags | join(', ') }}
+ {% endif %} +

+ {{ v.url_user }} +

+
+
{% if v.ids_data %} -
-

Details

+
+

Details

{% for k1, v1 in v.ids_data.items() %} diff --git a/requirements.txt b/requirements.txt index 6bf22a8..4dd59b2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,52 +1,39 @@ aiohttp==3.7.3 aiohttp-socks==0.5.5 +arabic-reshaper==2.1.1 async-timeout==3.0.1 attrs==20.3.0 -backports.functools-lru-cache==1.6.1 beautifulsoup4==4.9.3 bs4==0.0.1 -cairocffi==1.2.0 -CairoSVG==2.5.1 certifi==2020.12.5 -cffi==1.14.4 chardet==3.0.4 colorama==0.4.4 -cssselect2==0.4.1 -defusedxml==0.6.0 +future==0.18.2 html5lib==1.1 idna==2.10 -importlib-metadata==3.3.0 Jinja2==2.11.2 lxml==4.6.2 -Markdown==3.3.3 MarkupSafe==1.1.1 mock==4.0.2 multidict==5.1.0 -numpy==1.19.5 -pandas==1.2.0 -pdf-reports==0.3.2 Pillow==8.1.0 pycountry==20.7.3 -pycparser==2.20 -Pyphen==0.10.0 -pypugjs==5.9.8 +PyPDF2==1.26.0 PySocks==1.7.1 -python-dateutil==2.8.1 +python-bidi==0.4.2 python-socks==1.1.2 -pytz==2020.5 +reportlab==3.5.59 requests==2.25.1 requests-futures==1.0.0 six==1.15.0 socid-extractor==0.0.2 soupsieve==2.1 stem==1.8.0 -tinycss2==1.1.0 torrequest==0.1.0 -tqdm==4.55.1 +tqdm==4.55.0 typing-extensions==3.7.4.3 urllib3==1.26.2 -WeasyPrint==52.2 webencodings==0.5.1 +xhtml2pdf==0.2.5 XMind==1.2.0 yarl==1.6.3 -zipp==3.4.0 From 81e92b365b76bfc8e56c93ee11d818b932b7ae10 Mon Sep 17 00:00:00 2001 From: Ping-uu Date: Mon, 11 Jan 2021 14:47:58 +0100 Subject: [PATCH 4/4] test fix --- tests/test_report.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/test_report.py b/tests/test_report.py index 80dedac..1d4f0d1 100644 --- a/tests/test_report.py +++ b/tests/test_report.py @@ -5,7 +5,7 @@ import os import xmind -from maigret.report import save_csv_report_to_file, genxmindfile, save_html_report +from maigret.report import save_csv_report_to_file, genxmindfile, save_html_pdf_report from maigret.result import QueryResult, QueryStatus @@ -93,8 +93,7 @@ def test_html_report(): except: pass - save_html_report(TEST) - + save_html_pdf_report(TEST,filename=report_name,filenamepdf=None) assert os.path.exists(report_name) report_text = open(report_name).read() @@ -102,3 +101,13 @@ def test_html_report(): assert SUPPOSED_BRIEF in report_text assert SUPPOSED_GEO in report_text assert SUPPOSED_INTERESTS in report_text + +def test_pdf_report(): + report_name_pdf = 'report_alexaimephotographycars.pdf' + try: + os.remove(report_name_pdf) + except: + pass + + save_html_pdf_report(TEST,filename=None,filenamepdf=report_name_pdf) + assert os.path.exists(report_name_pdf)