-# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
[[package]]
name = "aioredis"
version = "1.3.1"
description = "asyncio (PEP 3156) Redis support"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "anyio"
version = "4.3.0"
description = "High level compatibility layer for multiple asynchronous event loop implementations"
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "asgiref"
version = "3.8.1"
description = "ASGI specs, helper code, and adapters"
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "asttokens"
version = "2.4.1"
description = "Annotate AST trees with source code positions"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "async-timeout"
version = "4.0.3"
description = "Timeout context manager for asyncio programs"
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
name = "attrs"
version = "23.2.0"
description = "Classes Without Boilerplate"
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
name = "autobahn"
version = "23.6.2"
description = "WebSocket client & server library, WAMP real-time framework"
-category = "main"
optional = false
python-versions = ">=3.9"
files = [
name = "automat"
version = "22.10.0"
description = "Self-service finite-state machines for the programmer on the go."
-category = "main"
optional = false
python-versions = "*"
files = [
name = "bigbluebutton-api-python"
version = "0.0.11"
description = "Python library that provides access to the API of BigBlueButton"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "boto3"
version = "1.34.91"
description = "The AWS SDK for Python"
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "botocore"
version = "1.34.91"
description = "Low-level, data-driven core of boto 3."
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "cairocffi"
version = "1.6.1"
description = "cffi-based cairo bindings for Python"
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
name = "cairosvg"
version = "2.7.1"
description = "A Simple SVG Converter based on Cairo"
-category = "main"
optional = false
python-versions = ">=3.5"
files = [
name = "certifi"
version = "2024.2.2"
description = "Python package for providing Mozilla's CA Bundle."
-category = "main"
optional = false
python-versions = ">=3.6"
files = [
name = "cffi"
version = "1.16.0"
description = "Foreign Function Interface for Python calling C code."
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "channels"
version = "3.0.4"
description = "Brings async, event-driven capabilities to Django. Django 2.2 and up only."
-category = "main"
optional = false
python-versions = ">=3.6"
files = [
name = "channels-redis"
version = "3.4.0"
description = "Redis-backed ASGI channel layer implementation"
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
cryptography = ["cryptography (>=1.3.0)"]
tests = ["async-generator", "async-timeout", "cryptography (>=1.3.0)", "pytest", "pytest-asyncio (==0.14.0)"]
+[[package]]
+name = "chardet"
+version = "5.2.0"
+description = "Universal encoding detector for Python 3"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"},
+ {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"},
+]
+
[[package]]
name = "charset-normalizer"
version = "3.3.2"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
-category = "main"
optional = false
python-versions = ">=3.7.0"
files = [
name = "click"
version = "8.1.7"
description = "Composable command line interface toolkit"
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
-category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
name = "constantly"
version = "23.10.4"
description = "Symbolic constants in Python"
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "cryptography"
version = "42.0.5"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
name = "cssselect2"
version = "0.7.0"
description = "CSS selectors for Python ElementTree"
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
name = "daphne"
version = "3.0.2"
description = "Django ASGI (HTTP/WebSocket) server"
-category = "main"
optional = false
python-versions = ">=3.6"
files = [
name = "decorator"
version = "5.1.1"
description = "Decorators for Humans"
-category = "main"
optional = false
python-versions = ">=3.5"
files = [
name = "defusedxml"
version = "0.7.1"
description = "XML bomb protection for Python stdlib modules"
-category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
name = "dj-pagination"
version = "2.5.0"
description = "Django + Pagination Made Easy"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "django"
version = "3.2.25"
description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design."
-category = "main"
optional = false
python-versions = ">=3.6"
files = [
name = "django-debug-toolbar"
version = "3.2.1"
description = "A configurable set of panels that display various debug information about the current request/response."
-category = "main"
optional = false
python-versions = ">=3.6"
files = [
name = "django-jazzmin"
version = "2.4.7"
description = "Drop-in theme for django admin, that utilises AdminLTE 3 & Bootstrap 4 to make yo' admin look jazzy"
-category = "main"
optional = false
python-versions = ">=3.6"
files = [
name = "django-json-rpc"
version = "0.7.1"
description = "A simple JSON-RPC implementation for Django"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "django-nvd3"
version = "0.9.7"
description = "Django NVD3 - Chart Library for d3.js"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "django-postman"
version = "4.2"
description = "User-to-User messaging system for Django, with gateway to AnonymousUser, moderation and thread management, user & exchange filters, inbox/sent/archives/trash folders, support for apps: auto-complete, notification, mailer."
-category = "main"
optional = false
python-versions = "*"
files = [
name = "django-recaptcha"
version = "2.0.6"
description = "Django recaptcha form field/widget app."
-category = "main"
optional = false
python-versions = "*"
files = [
name = "django-storages"
version = "1.14.2"
description = "Support for many storage backends in Django"
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
name = "django-tinymce"
version = "3.3.0"
description = "A Django application that contains a widget to render a form field as a TinyMCE editor."
-category = "main"
optional = false
python-versions = "*"
files = [
name = "django-unique-session"
version = "1.0"
description = "Unique session handler for Django"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "django-user-agents"
version = "0.4.0"
description = "A django package that allows easy identification of visitors' browser, operating system and device information (mobile phone, tablet or has touch capabilities)."
-category = "main"
optional = false
python-versions = "*"
files = [
name = "djangorestframework"
version = "3.13.1"
description = "Web APIs for Django, made easy."
-category = "main"
optional = false
python-versions = ">=3.6"
files = [
name = "docutils"
version = "0.17.1"
description = "Docutils -- Python Documentation Utilities"
-category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
name = "exceptiongroup"
version = "1.2.1"
description = "Backport of PEP 654 (exception groups)"
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
name = "executing"
version = "2.0.1"
description = "Get the currently executing AST node of a frame, and other information"
-category = "main"
optional = false
python-versions = ">=3.5"
files = [
name = "h11"
version = "0.14.0"
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
name = "hiredis"
version = "2.3.2"
description = "Python wrapper for hiredis"
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
name = "html5lib"
version = "1.1"
description = "HTML parser based on the WHATWG HTML specification"
-category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
name = "httpcore"
version = "0.16.3"
description = "A minimal low-level HTTP client."
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
anyio = ">=3.0,<5.0"
certifi = "*"
h11 = ">=0.13,<0.15"
-sniffio = ">=1.0.0,<2.0.0"
+sniffio = "==1.*"
[package.extras]
http2 = ["h2 (>=3,<5)"]
-socks = ["socksio (>=1.0.0,<2.0.0)"]
+socks = ["socksio (==1.*)"]
[[package]]
name = "httptools"
version = "0.6.1"
description = "A collection of framework independent HTTP protocol utils."
-category = "main"
optional = false
python-versions = ">=3.8.0"
files = [
name = "httpx"
version = "0.23.3"
description = "The next generation HTTP client."
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
[package.extras]
brotli = ["brotli", "brotlicffi"]
-cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<13)"]
+cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<13)"]
http2 = ["h2 (>=3,<5)"]
-socks = ["socksio (>=1.0.0,<2.0.0)"]
+socks = ["socksio (==1.*)"]
[[package]]
name = "hyperlink"
version = "21.0.0"
description = "A featureful, immutable, and correct URL for Python."
-category = "main"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
name = "idna"
version = "3.7"
description = "Internationalized Domain Names in Applications (IDNA)"
-category = "main"
optional = false
python-versions = ">=3.5"
files = [
name = "incremental"
version = "22.10.0"
description = "\"A small library that versions your Python projects.\""
-category = "main"
optional = false
python-versions = "*"
files = [
name = "ipython"
version = "8.23.0"
description = "IPython: Productive Interactive Computing"
-category = "main"
optional = false
python-versions = ">=3.10"
files = [
name = "jedi"
version = "0.19.1"
description = "An autocompletion tool for Python that can be used for text editors."
-category = "main"
optional = false
python-versions = ">=3.6"
files = [
name = "jinja2"
version = "3.1.3"
description = "A very fast and expressive template engine."
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
name = "jmespath"
version = "1.0.1"
description = "JSON Matching Expressions"
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
name = "jxmlease"
version = "1.0.3"
description = "jxmlease converts between XML and intelligent Python data structures."
-category = "main"
optional = false
python-versions = "*"
files = [
name = "markupsafe"
version = "2.1.5"
description = "Safely add untrusted strings to HTML/XML markup."
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
name = "matplotlib-inline"
version = "0.1.7"
description = "Inline Matplotlib backend for Jupyter"
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "msgpack"
version = "1.0.8"
description = "MessagePack serializer"
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "numpy"
version = "1.26.4"
description = "Fundamental package for array computing in Python"
-category = "main"
optional = false
python-versions = ">=3.9"
files = [
name = "parso"
version = "0.8.4"
description = "A Python Parser"
-category = "main"
optional = false
python-versions = ">=3.6"
files = [
name = "pexpect"
version = "4.9.0"
description = "Pexpect allows easy control of interactive console applications."
-category = "main"
optional = false
python-versions = "*"
files = [
name = "pillow"
version = "10.3.0"
description = "Python Imaging Library (Fork)"
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "prompt-toolkit"
version = "3.0.43"
description = "Library for building powerful interactive command lines in Python"
-category = "main"
optional = false
python-versions = ">=3.7.0"
files = [
name = "psycopg2"
version = "2.8.6"
description = "psycopg2 - Python-PostgreSQL Database Adapter"
-category = "main"
optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
files = [
name = "ptyprocess"
version = "0.7.0"
description = "Run a subprocess in a pseudo terminal"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "pure-eval"
version = "0.2.2"
description = "Safely evaluate AST nodes without side effects"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "pyasn1"
version = "0.6.0"
description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)"
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "pyasn1-modules"
version = "0.4.0"
description = "A collection of ASN.1-based protocols modules"
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "pycparser"
version = "2.22"
description = "C parser in Python"
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "pygments"
version = "2.17.2"
description = "Pygments is a syntax highlighting package written in Python."
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
name = "pymemcache"
version = "3.4.4"
description = "A comprehensive, fast, pure Python memcached client"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "pyopenssl"
version = "24.1.0"
description = "Python wrapper module around the OpenSSL library"
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
name = "pypdf"
version = "4.2.0"
description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files"
-category = "main"
optional = false
python-versions = ">=3.6"
files = [
name = "pyphen"
version = "0.15.0"
description = "Pure Python module to hyphenate text"
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "python-dateutil"
version = "2.9.0.post0"
description = "Extensions to the standard Python datetime module"
-category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
files = [
name = "python-dotenv"
version = "1.0.1"
description = "Read key-value pairs from a .env file and set them as environment variables"
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "python-nvd3"
version = "0.14.2"
description = "Python NVD3 - Chart Library for d3.js"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "python-slugify"
version = "1.1.4"
description = "A Python Slugify application that handles Unicode"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "pytz"
version = "2024.1"
description = "World timezone definitions, modern and historical"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "pyyaml"
version = "6.0.1"
description = "YAML parser and emitter for Python"
-category = "main"
optional = false
python-versions = ">=3.6"
files = [
name = "redis"
version = "3.5.3"
description = "Python client for Redis key-value store"
-category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
[package.extras]
hiredis = ["hiredis (>=0.1.3)"]
+[[package]]
+name = "reportlab"
+version = "4.2.0"
+description = "The Reportlab Toolkit"
+optional = false
+python-versions = "<4,>=3.7"
+files = [
+ {file = "reportlab-4.2.0-py3-none-any.whl", hash = "sha256:53630f9d25a7938def3e6a93d723b72a7a5921d34d23cf7a0930adeb2cb0e6c1"},
+ {file = "reportlab-4.2.0.tar.gz", hash = "sha256:474fb28d63431a5d47d75c90d580393050df7d491a09c7877df3291a2e9f6d0a"},
+]
+
+[package.dependencies]
+chardet = "*"
+pillow = ">=9.0.0"
+
+[package.extras]
+accel = ["rl-accel (>=0.9.0,<1.1)"]
+pycairo = ["freetype-py (>=2.3.0,<2.4)", "rlPyCairo (>=0.2.0,<1)"]
+renderpm = ["rl-renderPM (>=4.0.3,<4.1)"]
+
[[package]]
name = "requests"
version = "2.31.0"
description = "Python HTTP for Humans."
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
name = "rfc3986"
version = "1.5.0"
description = "Validating URI References per RFC 3986"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "s3transfer"
version = "0.10.1"
description = "An Amazon S3 Transfer Manager"
-category = "main"
optional = false
python-versions = ">= 3.8"
files = [
name = "service-identity"
version = "24.1.0"
description = "Service identity verification for pyOpenSSL & cryptography."
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "setuptools"
version = "69.5.1"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
-category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
name = "sniffio"
version = "1.3.1"
description = "Sniff out which async library your code is running under"
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
name = "sorl-thumbnail"
version = "12.10.0"
description = "Thumbnails for Django"
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "sqlparse"
version = "0.5.0"
description = "A non-validating SQL parser."
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "stack-data"
version = "0.6.3"
description = "Extract data from python stack frames and tracebacks for informative displays"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "tinycss2"
version = "1.3.0"
description = "A tiny CSS parser"
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "traitlets"
version = "5.14.3"
description = "Traitlets Python configuration system"
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "twisted"
version = "24.3.0"
description = "An asynchronous networking framework written in Python"
-category = "main"
optional = false
python-versions = ">=3.8.0"
files = [
name = "twisted-iocpsupport"
version = "1.0.4"
description = "An extension for use in the twisted I/O Completion Ports reactor."
-category = "main"
optional = false
python-versions = "*"
files = [
name = "txaio"
version = "23.1.1"
description = "Compatibility API between asyncio/Twisted/Trollius"
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
name = "typing-extensions"
version = "4.11.0"
description = "Backported and Experimental Type Hints for Python 3.8+"
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "ua-parser"
version = "0.18.0"
description = "Python port of Browserscope's user agent parser"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "unidecode"
version = "1.2.0"
description = "ASCII transliterations of Unicode text"
-category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
name = "urllib3"
version = "2.2.1"
description = "HTTP library with thread-safe connection pooling, file post, and more."
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "user-agents"
version = "2.2.0"
description = "A library to identify devices (phones, tablets) and their capabilities by parsing browser user agent strings."
-category = "main"
optional = false
python-versions = "*"
files = [
name = "uvicorn"
version = "0.18.1"
description = "The lightning-fast ASGI server."
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
httptools = {version = ">=0.4.0", optional = true, markers = "extra == \"standard\""}
python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""}
PyYAML = {version = ">=5.1", optional = true, markers = "extra == \"standard\""}
-uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""}
+uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""}
watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""}
websockets = {version = ">=10.0", optional = true, markers = "extra == \"standard\""}
name = "uvloop"
version = "0.19.0"
description = "Fast implementation of asyncio event loop on top of libuv"
-category = "main"
optional = false
python-versions = ">=3.8.0"
files = [
name = "uwsgi"
version = "2.0.25.1"
description = "The uWSGI server"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "watchfiles"
version = "0.21.0"
description = "Simple, modern and high performance file watching and code reload in python."
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "wcwidth"
version = "0.2.13"
description = "Measures the displayed width of unicode strings in a terminal"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "weasyprint"
version = "52.5"
description = "The Awesome Document Factory"
-category = "main"
optional = false
python-versions = ">=3.6"
files = [
name = "webencodings"
version = "0.5.1"
description = "Character encoding aliases for legacy web content"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "websockets"
version = "12.0"
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
-category = "main"
optional = false
python-versions = ">=3.8"
files = [
name = "xlrd"
version = "2.0.1"
description = "Library for developers to extract data from Microsoft Excel (tm) .xls spreadsheet files"
-category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
files = [
name = "xlwt"
version = "1.3.0"
description = "Library to create spreadsheet files compatible with MS Excel 97/2000/XP/2003 XLS files, on any platform, with Python 2.6, 2.7, 3.3+"
-category = "main"
optional = false
python-versions = "*"
files = [
name = "zope-interface"
version = "6.3"
description = "Interfaces for Python"
-category = "main"
optional = false
python-versions = ">=3.7"
files = [
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
-content-hash = "ecf8b0f59566e224cb558aacffda4f46926866a8d8471898ff1b032d0ab7009d"
+content-hash = "6bc6cbf6fbe9ed773cacf9451ece9f8e29997e5426ad0dddb40529ea90a7bf3c"
# Create your views here.
import datetime
+import io
import json
import numpy as np
from django.views.generic.base import View
from django.views.generic.edit import CreateView, UpdateView
from django.views.generic.list import ListView
+from pdfannotator.models import Annotation, AnnotationComment
+from pypdf import PdfReader, PdfWriter
+from reportlab.lib.pagesizes import A4
+from reportlab.lib.styles import getSampleStyleSheet
+from reportlab.lib.units import inch
+from reportlab.pdfgen import canvas
+from reportlab.platypus import Paragraph, SimpleDocTemplate, Spacer
+
from teleforma.decorators import access_required, staff_required
from teleforma.models.crfpa import Student
from teleforma.views.core import CourseAccessMixin, get_courses
def dispatch(self, *args, **kwargs):
return super(ScriptView, self).dispatch(*args, **kwargs)
+class ScriptDownloadView(ScriptMixinView, CourseAccessMixin):
+ """
+ download the script pdf with its annotations
+ """
+
+ def get(self, request, *args, **kwargs):
+ script = get_object_or_404(Script, id=kwargs['pk'])
+
+ packet = io.BytesIO()
+ can = canvas.Canvas(packet, pagesize=A4)
+
+ # store text to be added in the last page
+ bottom_texts = []
+
+ page = 1
+ for annotation in Annotation.objects.filter(uuid=script.uuid).order_by('page'):
+ if annotation.page != page:
+ # we may have to skip some pages if there are no annotations on it
+ for i in range(annotation.page - page):
+ # go to next page
+ can.showPage()
+ page = annotation.page
+
+ # set text to red
+ can.setFillColorRGB(1,0,0)
+
+ annotation_data = json.loads(annotation.content)
+ print(annotation_data)
+ text = "N/A"
+ # we handle only point (comment) and textbox (text) annotations
+ if annotation_data.get('type') == 'point':
+ print(annotation.uuid)
+ # print(uuid)
+ comment = AnnotationComment.objects.get(annotation_id=annotation.annotation_id, uuid=annotation.uuid)
+ text = comment.content
+ bottom_texts.append(text)
+ # add a number corresponding to the annotation
+ can.drawString(annotation_data['x'], -12 + A4[1] - annotation_data['y'], str(len(bottom_texts)))
+ elif annotation_data.get('type') == 'textbox':
+ can.drawString(annotation_data['x'], -12 + A4[1] - annotation_data['y'], annotation_data['content'])
+
+ can.save()
+
+ # create a new pdf with list of annotations
+ annotation_page = io.BytesIO()
+ doc = SimpleDocTemplate(annotation_page, pagesize=A4)
+ Story = []
+ styles = getSampleStyleSheet()
+ # for i in range(100):
+ for i, text in enumerate(bottom_texts):
+ comment = f"<strong>{i + 1}</strong> : {text}"
+ style = styles["Normal"]
+ p = Paragraph(comment, style)
+ Story.append(p)
+ Story.append(Spacer(1, 0.2*inch))
+ doc.build(Story)
+
+ #move to the beginning of the StringIO buffer
+ packet.seek(0)
+
+ # create a new PDF that contains reportlab content
+ new_pdf = PdfReader(packet)
+ # existing PDF
+ existing_pdf = PdfReader(script.file.path)
+
+ # new pdf, merged from reportlab pdf and existing pdf
+ output = PdfWriter()
+
+ # merge annotation layer pdf and existing pdf
+ for i, page in enumerate(existing_pdf.pages):
+ page.merge_page(new_pdf.pages[i])
+ output.add_page(page)
+
+ # finally, add annotations pages
+ new_pdf = PdfReader(annotation_page)
+ for page in new_pdf.pages:
+ output.add_page(page)
+
+ response = HttpResponse(content_type='application/pdf')
+ response['Content-Disposition'] = 'attachment; filename="%s"' % script.file.name
+ output.write(response)
+ return response
+
+
class ScriptsView(ScriptsListMixinView, ListView):
def dispatch(self, request, *args, **kwargs):
return super(ScoreCreateView, self).dispatch(request, *args, **kwargs)
+
def get_mass_students(request):
"""
data = {'results': res, 'pagination': {'more': False}}
return HttpResponse(json.dumps(data), content_type='application/json')
+
+
+