Spaces:
Runtime error
Runtime error
Download report.py from KnockKnockProject/README: direct link, hf CLI and curl.
- Browser
- Download file 12.1 kB
-
https://huggingface.co/spaces/KnockKnockProject/README/resolve/main/report.py
- Command line
-
hf download hf://spaces/KnockKnockProject/README/report.py
-
curl -L -o report.py https://huggingface.co/spaces/KnockKnockProject/README/resolve/main/report.py
12.1 kB
| import matplotlib.pyplot as plt | |
| from fpdf import FPDF | |
| import pandas as pd | |
| import uuid | |
| from langchain_core.output_parsers import StrOutputParser | |
| from langchain_core.messages import HumanMessage | |
| from langchain_ollama import ChatOllama | |
| import matplotlib.pyplot as plt | |
| import base64 | |
| from io import BytesIO | |
| from PIL import Image | |
| from datetime import datetime | |
| import random | |
| class ReportGenerator(FPDF): | |
| def __init__(self, start_date, end_date): | |
| super().__init__(unit='cm') | |
| self.start_date = start_date | |
| self.end_date = end_date | |
| self.model = ChatOllama(model="llava-phi3:3.8b", temperature=0) | |
| self.chain = self.prompt_func | self.model | StrOutputParser() | |
| def generate_months(self): | |
| # Convert input strings to datetime objects | |
| start = datetime.strptime(self.start_date, '%b %Y') | |
| end = datetime.strptime(self.end_date, '%b %Y') | |
| # Generate the list of month-year strings | |
| months = [] | |
| current = start | |
| while current <= end: | |
| months.append(current.strftime('%b %Y')) | |
| # Move to the next month | |
| if current.month == 12: | |
| current = current.replace(year=current.year + 1, month=1) | |
| else: | |
| current = current.replace(month=current.month + 1) | |
| # Generate random numbers between 20 and 100 for each month | |
| random_numbers = [random.randint(20, 100) for _ in range(len(months))] | |
| return months, random_numbers | |
| def convert_to_base64(pil_image): | |
| buffered = BytesIO() | |
| pil_image.save(buffered, format="PNG") # You can change the format if needed | |
| img_str = base64.b64encode(buffered.getvalue()).decode("utf-8") | |
| return img_str | |
| def create_graph_1(self): | |
| # Sample data | |
| x, y = self.generate_months() | |
| # Create a plot | |
| plt.figure(figsize=(10, 3)) # Adjust the figure size to fit the left space | |
| plt.gcf().set_facecolor('#9edbff') | |
| plt.plot(x, y, marker='s', color='#1b1464') | |
| for i, label in enumerate(y): | |
| plt.text(x[i], y[i] - 20, str(label) + "%", ha='center', va='bottom', fontsize=12, color='black') | |
| plt.ylabel("Helmet Not Use, in %", fontsize=10, weight='bold') | |
| plt.ylim(0, 100) | |
| # Save the plot as an image | |
| plt.savefig("./temp/graph_1.png", bbox_inches='tight') # Save with tight layout | |
| plt.close() | |
| def create_graph_2(self): | |
| data = [30, 20, 24, 26] # Values for the pie chart | |
| labels = ['Student', 'Passenger', 'Other', 'Family Member'] # Labels for the slices | |
| fig, ax = plt.subplots(figsize=(4, 4), facecolor='#9edbff') # Set canvas background | |
| #ax.set_facecolor('white') # Set the pie chart's background to white | |
| colors = ['#C96868', '#FADFA1', '#FFF4EA', '#7EACB5'] | |
| # Draw the pie chart with labels inside | |
| wedges, texts, autotexts = ax.pie( | |
| data, labels=None, # Exclude outer labels | |
| colors=colors, | |
| autopct='%1.1f%%', startangle=90, | |
| textprops=dict(color="black", fontsize=12, weight='bold') # Style the text inside | |
| ) | |
| # Add original labels to the pie wedges | |
| for i, autotext in enumerate(autotexts): | |
| autotext.set_text(f"{labels[i]}\n{autotext.get_text()}") # Label + percentage | |
| # Make sure the pie chart is circular | |
| ax.axis('equal') | |
| plt.savefig("./temp/graph_2.png", bbox_inches='tight') # Save the chart as an image | |
| plt.close() # Close the plot to free memory | |
| def bullet_multicell(self, width, height, bullet, text, bullet_indent=0.4): | |
| # Add the bullet and first line of text | |
| self.cell(bullet_indent, height, bullet) # Bullet with small space | |
| self.multi_cell(width - bullet_indent, height, text) | |
| # Adjust X position for wrapped lines (subsequent lines) | |
| last_y = self.get_y() | |
| self.set_xy(self.l_margin, last_y) # Indent wrapped lines | |
| return last_y | |
| def generate(self): | |
| self.set_auto_page_break(auto=True, margin=1.5) | |
| self.add_page() | |
| primary_color = (56, 182, 255) | |
| secondary_color = (27, 20, 100) | |
| self.image('./resources/banner.png', x=0, y=0, w=21.0) | |
| self.set_text_color(255, 255, 255) | |
| self.set_xy(1.0, 1.23) | |
| self.set_font('Arial', 'B', 24) | |
| self.cell(0, 0, 'Motorcycle Helmet Safety', ln=False, align='L') | |
| self.set_xy(1.0, 2.22) | |
| self.set_font('Arial', '', 18) | |
| self.cell(0, 0, 'Statistics Report', ln=False, align='L') | |
| self.set_xy(17.22, 1.73) | |
| self.set_font('Arial', '', 12) | |
| self.cell(0, 0.5, 'This document', ln=True, align='L') | |
| self.set_x(17.22) | |
| self.cell(0, 0.5, 'was made', ln=True, align='L') | |
| self.set_x(17.22) | |
| self.cell(0, 0.5, 'by Knock Knock', ln=True, align='L') | |
| self.set_draw_color(*primary_color) | |
| self.set_line_width(0.2) | |
| self.line(1.0 + 0.2, 3.39 - 0.2, 7.82, 3.39 - 0.2) | |
| self.set_text_color(0, 0, 0) | |
| date = f'{self.start_date} - {self.end_date}' | |
| self.set_xy(1.0, 4.3) | |
| self.set_font('Arial', '', 14) | |
| self.cell(0, 0.6, str(uuid.uuid4()), ln=False, align='L') | |
| self.cell(0, 0.6, date, ln=True, align='R') | |
| self.ln(0.25) | |
| self.set_font('Arial', 'b', 21) | |
| self.cell(0, 1, f'Motorcycle Helmet Non-use', ln=True, align='L') | |
| self.cell(0, 0.6, f'in {date} Overall Results', ln=True, align='L') | |
| self.ln(0.75) | |
| self.set_font('Arial', 'b', 18) | |
| self.cell(0, 0, 'Camera Information', ln=True, align='L') | |
| self.ln(0.5) | |
| self.set_x(1.5) | |
| self.set_font('Arial', 'b', 14) | |
| self.multi_cell(19, 0.6, f"{'HP 4M Fixed USB Camera'}", align='L') | |
| self.set_font('Arial', '', 14) | |
| self.set_x(2.0) | |
| self.bullet_multicell(19-2, 0.6, "-", f"IP: {'122.22.25.2'}") | |
| self.set_x(2.0) | |
| #self.multi_cell(19, 0.6, f"- Address: {'1 Chalong Krung 1 Alley, Lat Krabang, Khet Lat Krabang, Krung Thep Maha Nakhon 10520'}", align='J') | |
| self.bullet_multicell(19-2, 0.6, "-", f"Address: {'1 Chalong Krung 1 Alley, Lat Krabang, Khet Lat Krabang, Krung Thep Maha Nakhon 10520'}") | |
| self.set_x(2.0) | |
| self.bullet_multicell(19-2, 0.6, "-", f"Administrator: {'KMITL'}") | |
| self.ln(0.75) | |
| self.set_font('Arial', 'b', 18) | |
| self.cell(0, 0, 'Introduction', ln=True, align='L') | |
| self.ln(0.5) | |
| query = f"Write me 2-3 paragraphs about the introduction of Motorcycle Helmet Non-use less than 120 words" | |
| introduction = " "*4 + self.chain.invoke({"text": query, "image": ''}).strip().replace("\t", "").replace("\n", "\n"+" "*4) | |
| #summary = 3*(' '*4 + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.\n\n") | |
| self.set_font('Arial', '', 14) | |
| self.multi_cell(19, 0.6, introduction, align='J') | |
| self.set_draw_color(*secondary_color) | |
| self.set_line_width(0.15) | |
| self.line(1.0 + 0.15, 29 - 0.15, 20, 29 - 0.15) | |
| self.add_page() | |
| self.ln(0.25) | |
| self.set_font('Arial', '', 14) | |
| self.cell(0, 1.2, 'Figure 1', ln=True, align='L') | |
| self.set_font('Arial', 'b', 18) | |
| self.cell(0, 0, f'Motorcycle Helmet Non-use in {date}', ln=True, align='L') | |
| self.ln(0.4) | |
| self.create_graph_1() | |
| self.image("./temp/graph_1.png", x=1, w=19, h=7) | |
| self.ln(0.25) | |
| query = f"Analyze the chart depicting the percentage of motorcycle helmet non-use for the month of {date}. Please include key insights such as trends over time, comparisons with previous months or years, any noticeable spikes or drops, and possible reasons for these changes. Summarize your analysis in 1 short paragraph. less than 60 words" | |
| description_1 = " "*4 + self.chain.invoke({"text": query, "image": "./temp/graph_1.png"}).strip().replace("\t", "").replace("\n", "\n"+" "*4) | |
| #description_1 = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum." | |
| self.set_font('Arial', '', 14) | |
| self.multi_cell(19, 0.6, description_1, align='J') | |
| self.ln(0.1) | |
| self.set_font('Arial', '', 14) | |
| self.cell(0, 1.2, 'Figure 2', ln=True, align='L') | |
| self.set_font('Arial', 'b', 18) | |
| self.cell(0, 0, f'Motorcyclists, by Helmet Type', ln=True, align='L') | |
| y = self.get_y() | |
| self.ln(0.4) | |
| self.create_graph_2() | |
| self.image("./temp/graph_2.png", x=1, w=9, h=9) | |
| query = f"Analyze the pie chart illustrating the proportion of different categories of individuals who are not wearing motorcycle helmets Discuss Consider potential implications for safety measures or awareness campaigns. Summarize your insights in 1 short paragraph less than 30 words." | |
| description_2 = " "*4 + self.chain.invoke({"text": query, "image": "./temp/graph_2.png"}).strip().replace("\t", "").replace("\n", "\n"+" "*4) | |
| #description_2 = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum." | |
| self.set_font('Arial', '', 14) | |
| self.set_xy(10.5, y + 0.5) | |
| self.multi_cell(9.5, 0.6, description_2, align='J') | |
| self.set_draw_color(*secondary_color) | |
| self.set_line_width(0.15) | |
| self.line(1.0 + 0.15, 29 - 0.15, 20, 29 - 0.15) | |
| filename = f'./temp/motorcycle_helmet_report_{self.start_date}_{self.end_date}.pdf' | |
| self.output(filename) | |
| return filename | |
| def prompt_func(self, data): | |
| text = data["text"] | |
| image = data["image"] | |
| content_parts = [] | |
| text_part = {"type": "text", "text": text} | |
| content_parts.append(text_part) | |
| if image != '': | |
| image_part = { | |
| "type": "image_url", | |
| "image_url": f"data:image/jpeg;base64,{image}", | |
| } | |
| content_parts.append(image_part) | |
| return [HumanMessage(content=content_parts)] | |
| def describe_image(self, text, image_path): | |
| pil_image = Image.open(image_path) | |
| image_b64 = self.convert_to_base64(pil_image) | |
| query_chain = self.chain.invoke( | |
| {"text": text, "image": image_b64} | |
| ) | |
| return query_chain | |
| if __name__ == '__main__': | |
| report = ReportGenerator('NOV 2024', 'NOV 2025') | |
| #report.generate() | |
| x, y = report.generate_months() | |
| print(x, y) | |
| pass |