pythonplotlyorca

Alternative of orca for creating PDF file with images and figures


I am using this script to create pdf file and append images with statistics:

import MySQLdb
from plotly import graph_objs as go
import numpy as np
import os
from plotly.subplots import make_subplots
from PyPDF2 import PdfFileMerger
from datetime import datetime, timedelta
import smtplib
from email.message import EmailMessage
import imghdr

# Database connect
db = MySQLdb.connect(host="localhost",
                     user="root",
                     passwd="****",
                     db="ofasorgu_10_168_1_71")

today = datetime.today().strftime('%Y-%m-%d')
one_week = (datetime.today() - timedelta(days=7)).strftime('%Y-%m-%d')
two_week = (datetime.today() - timedelta(days=14)).strftime('%Y-%m-%d')
three_week = (datetime.today() - timedelta(days=21)).strftime('%Y-%m-%d')
four_week = (datetime.today() - timedelta(days=28)).strftime('%Y-%m-%d')

# Functions
def load_post_views(table, today, one_week, two_week, three_week, four_week):

    product_views_dict = dict()

    cursor = db.cursor()
    cursor.execute(
        "SELECT client_id, product_id, referrer, `date`" +
        " FROM " + table + 
        " WHERE `date`>='"+four_week+"'")

    for x in range(0, cursor.rowcount):

        row = cursor.fetchone()
        network = ""
        period = ""
        client_id = row[0]
        product_id = row[1]
        referrer = row[2]
        date = str(row[3])

        email_cursor = db.cursor()
        email_cursor.execute("SELECT address FROM c8ty_connections_email WHERE entry_id=" + str(client_id))
        email = email_cursor.fetchone()

        product_cursor = db.cursor()
        product_cursor.execute("SELECT post_title FROM c8ty_posts WHERE id=" + str(product_id))
        product_name = product_cursor.fetchone()

        # Add client ID key
        if client_id not in product_views_dict:
            product_views_dict[client_id] = dict()

        # Add product ID key to client ID parent key 
        if product_id not in product_views_dict[client_id]:
            product_views_dict[client_id][product_id] = {
                today + " - " + one_week: {
                    "facebook": 0,
                    "twitter": 0,
                    "instagram": 0,
                    "linkedin": 0,
                    "pinterest": 0,
                    "website": 0,
                },
                one_week + " - " + two_week: {
                    "facebook": 0,
                    "twitter": 0,
                    "instagram": 0,
                    "linkedin": 0,
                    "pinterest": 0,
                    "website": 0,
                },
                two_week + " - " + three_week: {
                    "facebook": 0,
                    "twitter": 0,
                    "instagram": 0,
                    "linkedin": 0,
                    "pinterest": 0,
                    "website": 0,
                },
                three_week + " - " + four_week: {
                    "facebook": 0,
                    "twitter": 0,
                    "instagram": 0,
                    "linkedin": 0,
                    "pinterest": 0,
                    "website": 0,
                }
            }

        # Find referrer
        if "facebook" in referrer:
            network = "facebook"
        elif "twitter" in referrer:
            network = "twitter"
        elif "instagram" in referrer:
            network = "instagram"
        elif "linkedin" in referrer:
            network = "linkedin"
        elif "pinterest" in referrer:
            network = "pinterest"
        else:
            network = "website"

        # Check view period
        if date <= today and date > one_week:
            period = today + " - " + one_week
        if date <= one_week and date > two_week:
            period = one_week + " - " + two_week
        if date <= two_week and date > three_week:
            period = two_week + " - " + three_week
        if date <= three_week and date > four_week:
            period = three_week + " - " + four_week

        product_views_dict[client_id][product_id][period][network] += 1 
        product_views_dict[client_id]["email"] = email[0]
        product_views_dict[client_id][product_id]["product"] = product_name[0]

    return product_views_dict

def draw_statistic(data_dict):
    for clinetID, product_info in data_dict.items():

        client_email = product_info["email"]

        for productID, product_data in product_info.items():

            if type(product_data) is dict:

                product_name = product_data['product']
                table_data = [
                    [
                        today      + " - " + one_week,
                        one_week   + " - " + two_week,
                        two_week   + " - " + three_week,
                        three_week + " - " + four_week,
                        today      + " - " + four_week
                    ]
                ]

                total_one = []
                total_two = []
                total_three = []
                total_four = []
                overall_traces = []

                networks_and_positions = [
                    {"network": "website","row": 2,"col": 1},
                    {"network": "linkedin","row": 2,"col": 2},
                    {"network": "facebook","row": 3,"col": 1},
                    {"network": "twitter","row": 3,"col": 2},
                    {"network": "instagram","row": 4,"col": 1},
                    {"network": "pinterest","row": 4,"col": 2}
                ]

                fig = make_subplots(rows=5, cols=2)
                merge_fig = make_subplots(rows=1, cols=1)
                count = 2

                for dictionary in networks_and_positions:

                    network = dictionary['network']
                    row = dictionary['row']
                    col = dictionary['col']

                    total_one.append(product_data[today + " - " + one_week][network])
                    total_two.append(product_data[one_week + " - " + two_week][network])
                    total_three.append(product_data[two_week + " - " + three_week][network])
                    total_four.append(product_data[three_week + " - " + four_week][network])

                    table_data.append([
                        product_data[today + " - " + one_week][network],
                        product_data[one_week + " - " + two_week][network],
                        product_data[two_week + " - " + three_week][network],
                        product_data[three_week + " - " + four_week][network],
                        sum([
                            product_data[today + " - " + one_week][network],
                            product_data[one_week + " - " + two_week][network], 
                            product_data[two_week + " - " + three_week][network], 
                            product_data[three_week + " - " + four_week][network]
                        ])
                    ])

                    xaxis = [
                                today      + " - " + one_week,
                                one_week   + " - " + two_week,
                                two_week   + " - " + three_week,
                                three_week + " - " + four_week,
                            ]

                    yaxis = [
                                product_data[today + " - " + one_week][network],
                                product_data[one_week + " - " + two_week][network],
                                product_data[two_week + " - " + three_week][network],
                                product_data[three_week + " - " + four_week][network]
                            ]

                    chart_name = network.capitalize() + " statistic" 

                    # Create bar chart
                    if (count == 2):
                        social_fig = go.Bar(
                                x=xaxis,
                                y=yaxis,
                                name=chart_name
                            )
                    else:
                        social_fig = go.Bar(
                                x=xaxis,
                                y=yaxis,
                                name=chart_name,
                                yaxis="y"+str(count)
                            ) 

                    # Add chart to fig
                    fig.add_trace(
                            social_fig,
                            row=row,
                            col=col
                        )

                    count += 1

                    trace_name = network.capitalize() + " views"
                    overall_traces.append(
                            go.Scatter(
                                x=xaxis,
                                y=yaxis,
                                name=trace_name
                            )
                        )

                total_column_values_array = [sum(total_one), sum(total_two), sum(total_three), sum(total_four),]

                total_column_values_array.append(
                        sum(total_one)+sum(total_two)+sum(total_three)+sum(total_four)
                    )

                table_data.append(total_column_values_array)

                # Create product table
                fig.add_trace(
                    go.Table(
                        header=dict(values=["Period", "Website", "Facebook", "Twitter", "Instagram", "LinkedIn", "Pinterest", "Total"]),
                        cells=dict(values=table_data)
                    )
                )

                merge_fig.add_traces(overall_traces)

                # Craete folder if doesn't exist
                if not os.path.exists("files"):
                    os.mkdir("files")

                statistic_file = "files/statistic_"+product_name+".pdf"
                overall_file = "files/overall_"+product_name+".pdf"
                out_file = "files/"+product_name+"_statistic.pdf"
                # Create charts file
                fig.update_layout(height=1500, width=1000, title_text="<b>Greetings</b><br />This is statistic from <a href='https://www.cfasuk.co.uk/'>CFAS UK</a> for your product <b>"+product_name+"</b>")
                fig.update_layout(
                        yaxis3=dict(title="Website views", titlefont=dict(color="#636efa")),
                        yaxis4=dict(title="LinkedIn views", titlefont=dict(color="#ef553b")),
                        yaxis5=dict(title="Facebook views", titlefont=dict(color="#00cc96")),
                        yaxis6=dict(title="Twitter views", titlefont=dict(color="#b780f9")),
                        yaxis7=dict(title="Instagram views", titlefont=dict(color="#ffa15a")),
                        yaxis8=dict(title="Pinterest views", titlefont=dict(color="#19d3f3")),
                    )
                fig.write_image(statistic_file)
                # Create overall file
                merge_fig.update_layout(height=700, width=1000, title_text="Overall <b>"+product_name+"</b> statistic")
                merge_fig.write_image(overall_file)
                merge = PdfFileMerger(strict=False)
                # Append charts file to merger
                merge.append(statistic_file)
                # Append overall file to merger
                merge.append(overall_file)
                # Create end statistic file with both charts and overall
                merge.write(out_file)
                merge.close()
                # Delete statistic file
                os.remove(statistic_file)
                # Delete overall file
                os.remove(overall_file)
                # Send email with file
                send_mail(
                    "tomaivanovtomov@gmail.com", 
                    "tomaivanovtomov@gmail.com", 
                    "CFAS UK, "+product_name+" statistic",
                    "This is automated email. Please, do not reply!<br/>"+
                    "If you find some problem with the statistic or the product is not yours, please contact CFAS UK team.<br/>"+
                    "Best regards!",
                    out_file
                )

def send_mail(send_from, send_to, subject, text, file=None):
    with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp:

        smtp.login("tomaivanovtomov@gmail.com", "zmjquvphuvigqdai")

        msg = EmailMessage()
        msg['Subject'] = subject
        msg['From'] = send_from
        msg['To'] = send_to
        msg.set_content(text)

        with open(file, "rb") as f:
            file_data = f.read()
            file_name = f.name
            msg.add_attachment(file_data, maintype='application/pdf', subtype='pdf', filename=file_name)

        smtp.send_message(msg)

# Init
product_views_dict = load_post_views("an_product_view", today, one_week, two_week, three_week, four_week)
brochure_views_dict = load_post_views("an_brochure_view", today, one_week, two_week, three_week, four_week)

draw_statistic(product_views_dict)
draw_statistic(brochure_views_dict)

db.close()
exit()

It works fine when I test it on my local server. But I need to upload it to shared account on a Centos server. On my hosting provider. There I can't install anaconda which I need to install orca for static images. Is there an alternative to create images and then add it to pdf file? Thank you in advance!


Solution

  • Yes (you do not need to use any tool backend dependency in your code), there's a very easy alternative library example as shown below.

    you can use matplotlib integration for this.

    you can import these modules as shown below

    from plotly.offline import init_notebook_mode, plot_mpl
    import matplotlib.pyplot as plt
    

    and you can use them as below:

    init_notebook_mode()
    fig = plt.figure()
    # you can configure you plot here  use below to save it as image
    
    plot_mpl(fig)
    plot_mpl(fig, image='png')
    

    OR if you want to still stick to only plotly and find alternative, you can see beow

    you can use a offline module of plotly to generate static images on the server and use it to generate a PDF. you can import it using from plotly.offline import plot. Once you've imported then you use the plot function as below

    fig = go.Figure( data=data, layout=layout )
    plot( fig, filename='your-file-name' , image = 'png')