contract-extractor / backend /scripts /make_demo_contract.py
myke69's picture
Add files using upload-large-folder tool
296a9b2 verified
Raw
History Blame Contribute Delete
9.56 kB
"""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()