Spaces:
Starting
Starting
| """Generate a realistic demo Master Services Agreement PDF. | |
| Deliberately seeded with the risk patterns the engine detects: | |
| uncapped liability, 90-day auto-renewal notice, one-sided termination, | |
| slow breach notification, exclusivity, high late-payment interest, | |
| sole-remedy SLA credits — so the demo tells the full story end-to-end. | |
| .venv/bin/python backend/scripts/make_demo_contract.py | |
| """ | |
| from __future__ import annotations | |
| import pathlib | |
| from reportlab.lib.pagesizes import A4 | |
| from reportlab.pdfbase.pdfmetrics import stringWidth | |
| from reportlab.pdfgen import canvas | |
| OUT = pathlib.Path(__file__).resolve().parents[2] / "data" / "samples" / "demo_msa.pdf" | |
| PARAS = [ | |
| ("T", "MASTER SERVICES AGREEMENT"), | |
| ("P", 'This Master Services Agreement (the "Agreement") is entered into as of ' | |
| 'July 1, 2026 (the "Effective Date") by and between Acme Industries Inc ' | |
| '("Client"), a Delaware corporation with offices at 100 Market Street, ' | |
| 'San Francisco, CA, and Globex Services Ltd ("Supplier"), a company ' | |
| 'registered in England and Wales with offices at 1 Canary Wharf, London. ' | |
| 'Client and Supplier are each a "Party" and together the "Parties".'), | |
| ("H", "1. Definitions"), | |
| ("P", '"Confidential Information" means all non-public information disclosed by ' | |
| 'one Party to the other, whether oral or written, that is designated as ' | |
| 'confidential or that reasonably should be understood to be confidential. ' | |
| '"Deliverables" means the work product described in a Statement of Work. ' | |
| '"Services" means the managed analytics platform services described in ' | |
| 'Exhibit A. "Fees" means the charges set out in Section 4.'), | |
| ("H", "2. Services and Deliverables"), | |
| ("P", "Supplier shall provide the Services to Client in accordance with this " | |
| "Agreement and each Statement of Work. Supplier shall deliver the initial " | |
| "implementation plan to Client no later than August 15, 2026. Supplier " | |
| "shall conduct quarterly business reviews with Client, the first such " | |
| "review to occur by October 30, 2026. Client shall provide Supplier with " | |
| "timely access to systems and personnel reasonably required to perform " | |
| "the Services."), | |
| ("H", "3. Exclusivity"), | |
| ("P", "During the Term, Client shall procure all managed analytics services " | |
| "exclusively from Supplier and shall not engage any third party to " | |
| "provide services that are substantially similar to the Services without " | |
| "Supplier's prior written consent."), | |
| ("H", "4. Fees and Payment"), | |
| ("P", "Client shall pay all undisputed Fees within sixty (60) days of receipt " | |
| "of a valid invoice (Net 60). Late payments shall accrue interest at 2% " | |
| "per month or the maximum rate permitted by law, whichever is lower. " | |
| "Supplier may increase the Fees by no more than 5% per year upon ninety " | |
| "(90) days' written notice. All Fees are exclusive of applicable taxes, " | |
| "which Client shall pay."), | |
| ("H", "5. Term and Renewal"), | |
| ("P", "This Agreement shall commence on the Effective Date and continue for an " | |
| "initial term of three (3) years, expiring on June 30, 2029 (the " | |
| '"Initial Term"). Thereafter this Agreement shall automatically renew ' | |
| "for successive twelve (12) month renewal terms unless either Party " | |
| "gives written notice of non-renewal at least ninety (90) days prior to " | |
| "the end of the then-current term."), | |
| ("H", "6. Termination"), | |
| ("P", "Supplier may terminate this Agreement for convenience upon sixty (60) " | |
| "days' written notice to Client. Either Party may terminate this " | |
| "Agreement upon thirty (30) days' written notice if the other Party " | |
| "materially breaches this Agreement and fails to cure such breach within " | |
| "the notice period. Upon termination, Client shall pay all Fees accrued " | |
| "through the effective date of termination."), | |
| ("H", "7. Service Levels"), | |
| ("P", "Supplier shall make the Services available 98.5% of the time in each " | |
| "calendar month, excluding scheduled maintenance. If availability falls " | |
| "below the foregoing commitment, Client shall be entitled to service " | |
| "credits as set out in Exhibit B, which credits shall be Client's sole " | |
| "and exclusive remedy for any failure to meet the service levels."), | |
| ("H", "8. Confidentiality"), | |
| ("P", "Each Party shall protect the other Party's Confidential Information " | |
| "using at least the same degree of care it uses for its own confidential " | |
| "information, and in no event less than reasonable care. This Section 8 " | |
| "shall survive termination of this Agreement for a period of five (5) " | |
| "years. Each Party shall promptly notify the other upon becoming aware " | |
| "of any unauthorized disclosure of Confidential Information."), | |
| ("H", "9. Data Protection"), | |
| ("P", "Supplier shall process Client personal data only on Client's documented " | |
| "instructions and in compliance with applicable data protection laws, " | |
| "including GDPR where applicable. Supplier shall notify Client of any " | |
| "security breach affecting Client personal data within seven (7) days of " | |
| "becoming aware of such breach. Supplier shall implement appropriate " | |
| "technical and organizational measures as described in Section 9 and " | |
| "Exhibit C."), | |
| ("H", "10. Indemnification"), | |
| ("P", "Supplier shall indemnify, defend and hold harmless Client from and " | |
| "against any and all claims, losses, liabilities and expenses arising " | |
| "out of or relating to the Services, except to the extent caused by " | |
| "Client's gross negligence. Client shall promptly notify Supplier of any " | |
| "claim subject to indemnification under this Section 10."), | |
| ("H", "11. Limitation of Liability"), | |
| ("P", "EXCEPT FOR A PARTY'S INDEMNIFICATION OBLIGATIONS, IN NO EVENT SHALL " | |
| "EITHER PARTY BE LIABLE FOR ANY INDIRECT, INCIDENTAL OR CONSEQUENTIAL " | |
| "DAMAGES. THE PARTIES AGREE THAT SUPPLIER'S LIABILITY FOR DIRECT DAMAGES " | |
| "SHALL NOT BE LIMITED AND NOTHING IN THIS AGREEMENT SHALL LIMIT " | |
| "SUPPLIER'S ABILITY TO RECOVER ALL AMOUNTS DUE."), | |
| ("H", "12. Insurance"), | |
| ("P", "Supplier shall maintain commercial general liability insurance with " | |
| "policy limits of not less than $2,000,000 per occurrence and shall " | |
| "provide certificates of insurance to Client upon request."), | |
| ("H", "13. Audit Rights"), | |
| ("P", "Client may, upon thirty (30) days' written notice and not more than " | |
| "once per year, audit Supplier's books and records solely as they relate " | |
| "to the Fees charged under this Agreement."), | |
| ("H", "14. Assignment"), | |
| ("P", "Neither Party may assign this Agreement without the prior written " | |
| "consent of the other Party, except that Supplier may assign this " | |
| "Agreement to an affiliate or in connection with a merger or sale of " | |
| "substantially all of its assets without consent."), | |
| ("H", "15. Governing Law"), | |
| ("P", "This Agreement shall be governed by the laws of England and Wales, and " | |
| "the Parties submit to the exclusive jurisdiction of the courts of " | |
| "London, England, subject to the dispute escalation process described " | |
| "in Section 15."), | |
| ("H", "16. Notices"), | |
| ("P", "All notices under this Agreement shall be in writing and delivered to " | |
| "the addresses set out above, and shall be deemed given upon receipt. " | |
| "Notices of non-renewal under Section 5 and termination under Section 6 " | |
| "must be sent by registered mail."), | |
| ] | |
| MARGIN = 56 | |
| WIDTH, HEIGHT = A4 # 595 x 842 points | |
| LINE_H = {"T": 22, "H": 16, "P": 13} | |
| FONT = {"T": ("Helvetica-Bold", 16), "H": ("Helvetica-Bold", 11.5), "P": ("Helvetica", 10)} | |
| GAP = {"T": 20, "H": 6, "P": 12} | |
| def wrap(text: str, fontname: str, size: float, width: float) -> list[str]: | |
| words = text.split() | |
| lines, cur = [], "" | |
| for w in words: | |
| trial = (cur + " " + w).strip() | |
| if stringWidth(trial, fontname, size) <= width: | |
| cur = trial | |
| else: | |
| if cur: | |
| lines.append(cur) | |
| cur = w | |
| if cur: | |
| lines.append(cur) | |
| return lines | |
| def main() -> None: | |
| OUT.parent.mkdir(parents=True, exist_ok=True) | |
| c = canvas.Canvas(str(OUT), pagesize=A4) | |
| pages = 1 | |
| y = MARGIN + 10 # measured from the TOP; reportlab draws from the bottom | |
| usable = WIDTH - 2 * MARGIN | |
| for kind, text in PARAS: | |
| fname, fsize = FONT[kind] | |
| lines = wrap(text, fname, fsize, usable) | |
| needed = len(lines) * LINE_H[kind] + GAP[kind] | |
| if y + needed > HEIGHT - MARGIN and kind != "T": | |
| c.showPage() | |
| pages += 1 | |
| y = MARGIN + 10 | |
| c.setFont(fname, fsize) | |
| for line in lines: | |
| c.drawString(MARGIN, HEIGHT - y, line) | |
| y += LINE_H[kind] | |
| y += GAP[kind] | |
| c.save() | |
| print(f"wrote {OUT} ({pages} pages)") | |
| if __name__ == "__main__": | |
| main() | |