Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

handle Spreadsheet and Upgrade to Debian 13 #39

Merged
merged 26 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
bb13e90
Prepartion to upgrade to debian 13 and library upgrades
philippe-levan Nov 7, 2024
b0c2d53
Add usage of Calc (ods, xlsx, ..) in lotemplate
cyrilus Dec 16, 2024
3c63aaa
now not dic for json
cyrilus Dec 26, 2024
bda4971
create unittest for calc
cyrilus Dec 27, 2024
2edeb51
refactor code for starting multi office and select random connexion
cyrilus Dec 28, 2024
6cb4dcb
readd restart in case libreoffice died unexpectedly
cyrilus Dec 28, 2024
adfc0d9
add exception for tmpfile
cyrilus Dec 28, 2024
890e595
fix import of function problem
cyrilus Dec 28, 2024
b64393d
ADD possibity of looping on table
cyrilus Jan 3, 2025
036d86f
ADD handle of deprecetion of dict in json
cyrilus Jan 8, 2025
3eb46df
Correction of starting libreoffice
cyrilus Jan 8, 2025
c0a3529
Add calc tests
cyrilus Jan 8, 2025
ae5360f
now use jsondiff to compare json + bug correction in Calc Statement
cyrilus Jan 9, 2025
0c7a994
add cache system for json plus unitest for it
cyrilus Jan 9, 2025
17f623e
refactor code for cache system
cyrilus Jan 10, 2025
ee6958a
add ruff linter in CI
cyrilus Jan 10, 2025
5d2629b
fixed code that was not closing the document when necessary and add f…
cyrilus Jan 12, 2025
fe33b9b
fix doc cli parameters
philippe-levan Jan 20, 2025
3e554c3
bugfix in the API when missing key in json
cyrilus Jan 20, 2025
f604e0d
MAXTIME default value
philippe-levan Jan 22, 2025
7b74c8a
fix bugs on error messages with the API
philippe-levan Jan 22, 2025
8386b85
Change the syntax of the named ranges : loop_down_xxx
philippe-levan Jan 22, 2025
0fca136
add usebruno tool
philippe-levan Jan 22, 2025
e70b23c
fix tests after renaming table_r/d by loop_down/right
philippe-levan Jan 22, 2025
bbf8773
Documentation
philippe-levan Jan 22, 2025
a178a1c
fix ruff
philippe-levan Jan 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/usebruno
/venv
/uploads
/exports
/.vscode
/.local
/.github
/.config
/.cache
4 changes: 3 additions & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
SECRET_KEY=DEFAULT_KEY
NB_WORKERS=6
MAXTIME=120
SECRET_KEY=DEFAULT_KEY
8 changes: 8 additions & 0 deletions .github/workflows/ruff.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
name: Ruff
on: [ push, pull_request ]
jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/ruff-action@v3
2 changes: 1 addition & 1 deletion .github/workflows/unittest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0

Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
__pycache__
.bash_history
*.swp
.viminfo
/.cache/
/.config/
.local
Expand All @@ -10,16 +12,21 @@ uploads/
.fleet/
.vscode/
.~lock.*#
.python_history
exports/
/config*
/lotemplate/unittest/files/content/*.unittest.txt
/lotemplate/unittest/files/content/*.unittest.odt
/lotemplate/unittest/files/content/*.unittest.html
/lotemplate/unittest/files/content/debug.docx
/lotemplate/unittest/files/content/debug.odt
/lotemplate/unittest/files/content/debug.json
/lotemplate/unittest/files/content/*.unittest.odt
/lotemplate/unittest/files/content/*.unittest.pdf
/venv
/lotemplate/unittest/files/content/e89fbedb61af3994184da3e5340bd9e9-calc_variables.ods.json
/output*.*
.fontconfig/
docker-compose.override.yml
/tmpfile/**
!/tmpfile/.keep
194 changes: 76 additions & 118 deletions API/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,40 @@
Copyright (C) 2023 Probesys
"""

from flask import *
from flask import Response, send_file

import lotemplate as ot

import configargparse as cparse
import glob
import os
import sys
import subprocess
from time import sleep
from typing import Union
from zipfile import ZipFile

p = cparse.ArgumentParser(default_config_files=['config.yml', 'config.ini', 'config'])
p.add_argument('--config', '-c', is_config_file=True, help='Configuration file path')
p.add_argument('--host', default="localhost", help='Host address to use for the libreoffice connection')
p.add_argument('--port', default="2002", help='Port to use for the libreoffice connexion')
args = p.parse_known_args()[0]

os.makedirs("uploads", exist_ok=True)
os.makedirs("exports", exist_ok=True)
subprocess.call(
f'soffice "--accept=socket,host={args.host},port={args.port};urp;StarOffice.ServiceManager" &', shell=True)
sleep(3)
cnx = ot.Connexion(args.host, args.port)

host='localhost'
port='200'
gworkers=0
scannedjson=''
maxtime=60
def start_soffice(workers,jsondir,maxt=60):
global gworkers
global my_lo
global scannedjson
global maxtime
maxtime=maxt
scannedjson=jsondir
gworkers=workers
os.makedirs("uploads", exist_ok=True)
os.makedirs("exports", exist_ok=True)
os.makedirs(scannedjson, exist_ok=True)
clean_temp_files()
my_lo=ot.start_multi_office(nb_env=workers)

def restart_soffice() -> None:
"""
simply restart the soffice process

:return: None
"""

clean_temp_files()
subprocess.call(
f'soffice "--accept=socket,host={cnx.host},port={cnx.port};urp;StarOffice.ServiceManager" &',
shell=True
)
sleep(2)
try:
cnx.restart()
except:
pass
def connexion():
global my_lo
cnx= ot.randomConnexion(my_lo)

return cnx

def clean_temp_files():
"""
Expand Down Expand Up @@ -103,7 +92,6 @@ def error_format(exception: Exception, message: str = None) -> dict:
{
'error': type(exception).__name__,
'code': exception.code if isinstance(exception, ot.errors.LotemplateError) else type(exception).__name__,
# 'message': message or str(exception),
'message': message or exception_message,
'variables': exception.infos if isinstance(exception, ot.errors.LotemplateError) else {}
}
Expand Down Expand Up @@ -148,23 +136,16 @@ def save_file(directory: str, f, name: str, error_caught=False) -> Union[tuple[d
i += 1
f.stream.seek(0)
f.save(f"uploads/{directory}/{name}")


cnx = connexion()
global scannedjson
try:
with ot.Template(f"uploads/{directory}/{name}", cnx, True) as temp:
with ot.TemplateFromExt(f"uploads/{directory}/{name}", cnx, True,scannedjson) as temp:
values = temp.variables
except ot.errors.TemplateError as e:
delete_file(directory, name)
return error_format(e), 415
except ot.errors.UnoException as e:
delete_file(directory, name)
restart_soffice()
if error_caught:
return (
error_format(e, "Internal server error on file opening. Please checks the README file, section "
"'Unsolvable problems' for more informations."),
500
)
else:
return save_file(directory, f, name, True)
except Exception as e:
delete_file(directory, name)
return error_format(e), 500
Expand All @@ -180,24 +161,14 @@ def scan_file(directory: str, file: str, error_caught=False) -> Union[tuple[dict
:param error_caught: specify if an error was already caught
:return: a json and optionally an int which represent the status code to return
"""

try:
with ot.Template(f"uploads/{directory}/{file}", cnx, True) as temp:
cnx = connexion()
global scannedjson
with ot.TemplateFromExt(f"uploads/{directory}/{file}", cnx, True,scannedjson) as temp:
variables = temp.variables
except ot.errors.UnoException as e:
restart_soffice()
if error_caught:
return (
error_format(e, "Internal server error on file opening. Please checks the README file, section "
"'Unsolvable problems' for more informations."),
500
)
else:
return scan_file(directory, file, True)
return {'file': file, 'message': "Successfully scanned", 'variables': variables}


def fill_file(directory: str, file: str, json, error_caught=False) -> Union[tuple[dict, int], dict, Response]:
def fill_file(directory: str, file: str, json, error_caught=False) -> Union[tuple[dict, int], dict, tuple[str,Response]]:
"""
fill the specified file

Expand All @@ -208,61 +179,48 @@ def fill_file(directory: str, file: str, json, error_caught=False) -> Union[tupl
:return: a json and optionally an int which represent the status code to return
"""

if type(json) != list or not json:
return error_sim("JsonSyntaxError", 'api_invalid_base_value_type', "The json should be a non-empty array"), 415

if isinstance(json, list):
json=json[0]
print("####\nUsing a list of dict is DEPRECATED, you must directly send the dict.")
print("See documentation.\n#######")
cnx = connexion()
global scannedjson
try:
with ot.Template(f"uploads/{directory}/{file}", cnx, True) as temp:

exports = []

for elem in json:

length = len(elem)
is_name_present = type(elem.get("name")) is str
is_variables_present = type(elem.get("variables")) is dict
is_page_break_present = type(elem.get("page_break")) is bool

if (
not is_name_present
or not is_variables_present
or ((length > 2 and not is_page_break_present) or (length > 3 and is_page_break_present))
):
return error_sim(
"JsonSyntaxError",
'api_invalid_instance_syntax',
"Each instance of the array in the json should be an object containing only 'name' - "
"a non-empty string, 'variables' - a non-empty object, and, optionally, 'page_break' - "
"a boolean."), 415

try:
json_variables = ot.convert_to_datas_template(elem["variables"])
temp.search_error(json_variables)
temp.fill(elem["variables"])
if elem.get('page_break', False):
temp.page_break()
exports.append(temp.export("exports/" + elem["name"], should_replace=(
True if len(json) == 1 else False)))
except Exception as e:
return error_format(e), 415

if len(exports) == 1:
return send_file(exports[0], download_name=exports[0].split("/")[-1])
else:
with ZipFile('exports/export.zip', 'w') as zipped:
for elem2 in exports:
zipped.write(elem2, elem2.split("/")[-1])
return send_file('exports/export.zip', 'export.zip')
except ot.errors.UnoException as e:
restart_soffice()
if error_caught:
return (
error_format(e, "Internal server error on file opening. Please checks the README file, section "
"'Unsolvable problems' for more informations."),
500
)
else:
return fill_file(directory, file, json, True)


clean_temp_files()
with ot.TemplateFromExt(f"uploads/{directory}/{file}", cnx, True,scannedjson) as temp:

length = len(json)
is_name_present = type(json.get("name")) is str
is_variables_present = type(json.get("variables")) is dict
is_page_break_present = type(json.get("page_break")) is bool

if (
not is_name_present
or not is_variables_present
or ((length > 2 and not is_page_break_present) or (length > 3 and is_page_break_present))
):
return 415, error_sim(
"JsonSyntaxError",
'api_invalid_instance_syntax',
"Each instance of the array in the json should be an object containing only 'name' - "
"a non-empty string, 'variables' - a non-empty object, and, optionally, 'page_break' - "
"a boolean.")

try:
json_variables = ot.convert_to_datas_template(json["variables"])
temp.search_error(json_variables)
temp.fill(json["variables"])
if json.get('page_break', False):
temp.page_break()
export_file=temp.export(json["name"],"exports")
export_name=json["name"]
except Exception as e:
if 'export_name' in locals():
return ( export_file,error_format(e))
else:
return ( "nofile",error_format(e))

return (export_file,send_file(export_file, export_name))

except Exception as e:
return error_format(e), 500

75 changes: 75 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
Versions
========

Note : the upgrade from version 1.x to 2.x is easy. There is no reason to stay to version 1.x.

The upgrade documentation is in the file [UPGRADE.md](UPGRADE.md).

Versions 2.x
------------

- v2.0.0 : 01/01/2025
- BC Break (easy to fix) : see [UPGRADE.md](UPGRADE.md)
- We can now generate Calc / Excel files (from Calc templates)
- Is multiThreaded : we can generate several files at the same time
- Performances improvements
- No BC Breaks for the templates
- upgrade debian, LibreOffice, Python libs versions
- for devs : added "use bruno" requests inside the repository

Versions 1.x
------------

- v1.6.1 : 2024-04-12 : bugfix
- fix the issue https://github.com/Probesys/lotemplate/issues/34 : too many endif bugg
- v1.6.0 : 2024-04-11
- allow put variables inside headers and footers
- fix a bug when a variable is both inside the text content and inside a table (it should not arrive, but it is fixed)
- a new unit test system based on PDF converted to text in order to test contents that are not converted to text with a simple saveAs
- v1.5.2 : 2024-02-24 : Better README
- Rewrite for a betterdocker DockerFile without bug
- v1.5.1 : 2024-02-16 : Better README
- Rewriting of the README file
- v1.5.0 : 2024-02-12 : syntax error detection
- add syntax error detection in if statements
- add syntax error detection in for statements
- come back to default libreoffice of Debian Bookworm (removed backports, incompatibility)
- v1.4.1 : 2023-11-20 : micro-feature for counter and fix possible bug
- use counters for counting elements of a list
- fix possible bug with reset and last.
- v1.4.0, 2023-11-17 : counters
- add a counter system inside templates
- add better scan for if statement. Raises an error if there is too many endif in the template.
- speedup html statement replacement and scanning
- speedup for statement replacement and scanning
- tests of for scanning
- internal : add scan testing inside content unit tests
- v1.3.0, 2023-11-16 :
- major refactoring. No evolution for the user.
- new unit tests on tables and images
- no BC Break (theoretically)
- v1.2.8, 2023-09-01 :
- fix bug in TextShape var replacement
- v1.2.7, 2023-08-30 :
- Upgrade to debian bookworm slim
- v1.2.6, 2023-08-30 :
- new comparators for if statements : ===, !==, CONTAINS, NOT_CONTAINS
- variables of type "html" are now supported and copied as HTML
- v1.2.5, 2023-07-17 : temporary fix for detecting endhtml and endfor
- v1.2.4, 2023-07-09 : fix major bug in if statement scanning
- v1.2.3, 2023-07-07 : no endif detection, performance improvement in if statement
- v1.2.2, 2023-06-09 : bugfix html statement scan missing
- v1.2.1, 2023-06-05 : little fix for CI
- v1.2.0, 2023-06-04 : if statements inside for
- v1.1.0, 2023-05-23 : recursive if statement
- v1.0.1, 2023-05-05 : workaround, fix in html formatting
- v1.0.0, 2023-05-03 : if statement, for statement, html statement
- not numbered : about may 2022 : first version

### Possible futur evolutions

- Possibly to add dynamic images in tables
- another way to make image variables that would be compatible with Microsoft Word and maybe other formats (example : set the variable name in the 'alternative text' field)
- key system for each institution for security
- handle bulleted lists using table like variables
- use variable formatting instead of the one of the character before
5 changes: 4 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
FROM debian:bookworm-slim as prod
FROM debian:trixie-slim as prod

RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache

RUN --mount=type=cache,id=apt-cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,id=apt-lib,target=/var/lib/apt,sharing=locked \
--mount=type=cache,id=debconf,target=/var/cache/debconf,sharing=locked \
Expand Down
Loading
Loading