From 1a23ef535373bc9d6f7c2e5992d0f5888e2a9c7b Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Tue, 9 Sep 2025 11:25:34 +0200 Subject: [PATCH 01/43] github workflow --- .github/workflows/deploy.yml | 12 ++++++++++++ .github/workflows/merge_request.yml | 9 +++++++++ .github/workflows/pr_update.yml | 11 +++++++++++ .github/workflows/prepare_deploy.yml | 9 +++++++++ .github/workflows/test.yml | 14 ++++++++++++++ .github/workflows/test_pr.yml | 12 ++++++++++++ 6 files changed, 67 insertions(+) create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/merge_request.yml create mode 100644 .github/workflows/pr_update.yml create mode 100644 .github/workflows/prepare_deploy.yml create mode 100644 .github/workflows/test.yml create mode 100644 .github/workflows/test_pr.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..35ffc1c --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,12 @@ +name: Deploy + +on: + workflow_dispatch: + +jobs: + deploy: + uses: Geode-solutions/actions/.github/workflows/py-deploy.yml@master + with: + npm: true + secrets: inherit + \ No newline at end of file diff --git a/.github/workflows/merge_request.yml b/.github/workflows/merge_request.yml new file mode 100644 index 0000000..663fa32 --- /dev/null +++ b/.github/workflows/merge_request.yml @@ -0,0 +1,9 @@ +name: Merge request + +on: + workflow_dispatch: + +jobs: + release: + uses: Geode-solutions/actions/.github/workflows/py-merge-request.yml@master + secrets: inherit diff --git a/.github/workflows/pr_update.yml b/.github/workflows/pr_update.yml new file mode 100644 index 0000000..9743601 --- /dev/null +++ b/.github/workflows/pr_update.yml @@ -0,0 +1,11 @@ +name: Pull request + +on: + pull_request: + types: [opened, reopened] + branches: + - master + +jobs: + update-branch: + uses: Geode-solutions/actions/.github/workflows/update-branch.yml@master \ No newline at end of file diff --git a/.github/workflows/prepare_deploy.yml b/.github/workflows/prepare_deploy.yml new file mode 100644 index 0000000..4c8536c --- /dev/null +++ b/.github/workflows/prepare_deploy.yml @@ -0,0 +1,9 @@ +name: Prepare deploy + +on: + workflow_dispatch: + +jobs: + prepare: + uses: Geode-solutions/actions/.github/workflows/py-prepare-deploy.yml@master + secrets: inherit diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..22c4bdb --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,14 @@ +name: Test + +on: + push: + branches-ignore: + - master + - next + +jobs: + test: + uses: Geode-solutions/actions/.github/workflows/py-test.yml@master + with: + npm: true + secrets: inherit diff --git a/.github/workflows/test_pr.yml b/.github/workflows/test_pr.yml new file mode 100644 index 0000000..43d7b73 --- /dev/null +++ b/.github/workflows/test_pr.yml @@ -0,0 +1,12 @@ +name: Test PR + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + +jobs: + test: + uses: Geode-solutions/actions/.github/workflows/py-test-pr.yml@master + with: + npm: true + secrets: inherit From 2a34d8bf49554e1c01dba5451119bae2e229abd6 Mon Sep 17 00:00:00 2001 From: MaxNumerique <144453705+MaxNumerique@users.noreply.github.com> Date: Tue, 9 Sep 2025 10:00:23 +0000 Subject: [PATCH 02/43] Apply prepare changes --- .pylintrc | 536 +++++++++++++++++++++++++++++++++++++++++++ .releaserc | 11 + commitlint.config.js | 16 ++ mypy.ini | 3 + 4 files changed, 566 insertions(+) create mode 100644 .pylintrc create mode 100644 .releaserc create mode 100644 commitlint.config.js create mode 100644 mypy.ini diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..bc7a268 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,536 @@ +[tool.pylint.main] +# Analyse import fallback blocks. This can be used to support both Python 2 and 3 +# compatible code, which means that the block might have code that exists only in +# one or another interpreter, leading to false positives when analysed. +# analyse-fallback-blocks = + +# Clear in-memory caches upon conclusion of linting. Useful if running pylint in +# a server-like mode. +# clear-cache-post-run = + +# Always return a 0 (non-error) status code, even if lint errors are found. This +# is primarily useful in continuous integration scripts. +# exit-zero = + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. +# extension-pkg-allow-list = + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. (This is an alternative name to extension-pkg-allow-list +# for backward compatibility.) +# extension-pkg-whitelist = + +# Return non-zero exit code if any of these messages/categories are detected, +# even if score is above --fail-under value. Syntax same as enable. Messages +# specified are enabled, while categories only check already-enabled messages. +# fail-on = + +# Specify a score threshold under which the program will exit with error. +fail-under = 10 + +# Interpret the stdin as a python script, whose filename needs to be passed as +# the module_or_package argument. +# from-stdin = + +# Files or directories to be skipped. They should be base names, not paths. +ignore = ["CVS"] + +# Add files or directories matching the regular expressions patterns to the +# ignore-list. The regex matches against paths and can be in Posix or Windows +# format. Because '\\' represents the directory delimiter on Windows systems, it +# can't be used as an escape character. +# ignore-paths = + +# Files or directories matching the regular expression patterns are skipped. The +# regex matches against base names, not paths. The default value ignores Emacs +# file locks +ignore-patterns = ["^\\.#"] + +# List of module names for which member attributes should not be checked (useful +# for modules/projects where namespaces are manipulated during runtime and thus +# existing member attributes cannot be deduced by static analysis). It supports +# qualified module names, as well as Unix pattern matching. +# ignored-modules = + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +# init-hook = + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use, and will cap the count on Windows to +# avoid hangs. +jobs = 1 + +# Control the amount of potential inferred values when inferring a single object. +# This can help the performance when dealing with large functions or complex, +# nested conditions. +limit-inference-results = 100 + +# List of plugins (as comma separated values of python module names) to load, +# usually to register additional checkers. +# load-plugins = + +# Pickle collected data for later comparisons. +persistent = true + +# Minimum Python version to use for version dependent checks. Will default to the +# version used to run pylint. +py-version = "3.9" + +# Discover python modules and packages in the file system subtree. +# recursive = + +# Add paths to the list of the source roots. Supports globbing patterns. The +# source root is an absolute path or a path relative to the current working +# directory used to determine a package namespace for modules located under the +# source root. +# source-roots = + +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages. +suggestion-mode = true + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +# unsafe-load-any-extension = + +[tool.pylint.basic] +# Naming style matching correct argument names. +argument-naming-style = "snake_case" + +# Regular expression matching correct argument names. Overrides argument-naming- +# style. If left empty, argument names will be checked with the set naming style. +# argument-rgx = + +# Naming style matching correct attribute names. +attr-naming-style = "snake_case" + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style. If left empty, attribute names will be checked with the set naming +# style. +# attr-rgx = + +# Bad variable names which should always be refused, separated by a comma. +bad-names = ["foo", "bar", "baz", "toto", "tutu", "tata"] + +# Bad variable names regexes, separated by a comma. If names match any regex, +# they will always be refused +# bad-names-rgxs = + +# Naming style matching correct class attribute names. +class-attribute-naming-style = "any" + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style. If left empty, class attribute names will be checked +# with the set naming style. +# class-attribute-rgx = + +# Naming style matching correct class constant names. +class-const-naming-style = "UPPER_CASE" + +# Regular expression matching correct class constant names. Overrides class- +# const-naming-style. If left empty, class constant names will be checked with +# the set naming style. +# class-const-rgx = + +# Naming style matching correct class names. +class-naming-style = "PascalCase" + +# Regular expression matching correct class names. Overrides class-naming-style. +# If left empty, class names will be checked with the set naming style. +# class-rgx = + +# Naming style matching correct constant names. +const-naming-style = "UPPER_CASE" + +# Regular expression matching correct constant names. Overrides const-naming- +# style. If left empty, constant names will be checked with the set naming style. +# const-rgx = + +# Minimum line length for functions/classes that require docstrings, shorter ones +# are exempt. +docstring-min-length = -1 + +# Naming style matching correct function names. +function-naming-style = "snake_case" + +# Regular expression matching correct function names. Overrides function-naming- +# style. If left empty, function names will be checked with the set naming style. +# function-rgx = + +# Good variable names which should always be accepted, separated by a comma. +good-names = ["i", "j", "k", "ex", "Run", "_"] + +# Good variable names regexes, separated by a comma. If names match any regex, +# they will always be accepted +# good-names-rgxs = + +# Include a hint for the correct naming format with invalid-name. +# include-naming-hint = + +# Naming style matching correct inline iteration names. +inlinevar-naming-style = "any" + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style. If left empty, inline iteration names will be checked +# with the set naming style. +# inlinevar-rgx = + +# Naming style matching correct method names. +method-naming-style = "snake_case" + +# Regular expression matching correct method names. Overrides method-naming- +# style. If left empty, method names will be checked with the set naming style. +# method-rgx = + +# Naming style matching correct module names. +module-naming-style = "snake_case" + +# Regular expression matching correct module names. Overrides module-naming- +# style. If left empty, module names will be checked with the set naming style. +# module-rgx = + +# Colon-delimited sets of names that determine each other's naming style when the +# name regexes allow several styles. +# name-group = + +# Regular expression which should only match function or class names that do not +# require a docstring. +no-docstring-rgx = "^_" + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. These +# decorators are taken in consideration only for invalid-name. +property-classes = ["abc.abstractproperty"] + +# Regular expression matching correct type alias names. If left empty, type alias +# names will be checked with the set naming style. +# typealias-rgx = + +# Regular expression matching correct type variable names. If left empty, type +# variable names will be checked with the set naming style. +# typevar-rgx = + +# Naming style matching correct variable names. +variable-naming-style = "snake_case" + +# Regular expression matching correct variable names. Overrides variable-naming- +# style. If left empty, variable names will be checked with the set naming style. +# variable-rgx = + +[tool.pylint.classes] +# Warn about protected attribute access inside special methods +# check-protected-access-in-special-methods = + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods = ["__init__", "__new__", "setUp", "asyncSetUp", "__post_init__"] + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected = ["_asdict", "_fields", "_replace", "_source", "_make", "os._exit"] + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg = ["cls"] + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg = ["mcs"] + +[tool.pylint.design] +# List of regular expressions of class ancestor names to ignore when counting +# public methods (see R0903) +# exclude-too-few-public-methods = + +# List of qualified class names to ignore when counting class parents (see R0901) +# ignored-parents = + +# Maximum number of arguments for function / method. +max-args = 5 + +# Maximum number of attributes for a class (see R0902). +max-attributes = 7 + +# Maximum number of boolean expressions in an if statement (see R0916). +max-bool-expr = 5 + +# Maximum number of branch for function / method body. +max-branches = 12 + +# Maximum number of locals for function / method body. +max-locals = 15 + +# Maximum number of parents for a class (see R0901). +max-parents = 7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods = 20 + +# Maximum number of return / yield for function / method body. +max-returns = 6 + +# Maximum number of statements in function / method body. +max-statements = 50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods = 2 + +[tool.pylint.exceptions] +# Exceptions that will emit a warning when caught. +overgeneral-exceptions = ["builtins.BaseException", "builtins.Exception"] + +[tool.pylint.format] +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +# expected-line-ending-format = + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines = "^\\s*(# )??$" + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren = 4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string = " " + +# Maximum number of characters on a single line. +max-line-length = 100 + +# Maximum number of lines in a module. +max-module-lines = 1000 + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +# single-line-class-stmt = + +# Allow the body of an if to be on the same line as the test if there is no else. +# single-line-if-stmt = + +[tool.pylint.imports] +# List of modules that can be imported at any level, not just the top level one. +# allow-any-import-level = + +# Allow explicit reexports by alias from a package __init__. +# allow-reexport-from-package = + +# Allow wildcard imports from modules that define __all__. +# allow-wildcard-with-all = + +# Deprecated modules which should not be used, separated by a comma. +# deprecated-modules = + +# Output a graph (.gv or any supported image format) of external dependencies to +# the given file (report RP0402 must not be disabled). +# ext-import-graph = + +# Output a graph (.gv or any supported image format) of all (i.e. internal and +# external) dependencies to the given file (report RP0402 must not be disabled). +# import-graph = + +# Output a graph (.gv or any supported image format) of internal dependencies to +# the given file (report RP0402 must not be disabled). +# int-import-graph = + +# Force import order to recognize a module as part of the standard compatibility +# libraries. +# known-standard-library = + +# Force import order to recognize a module as part of a third party library. +known-third-party = ["enchant"] + +# Couples of modules and preferred modules, separated by a comma. +# preferred-modules = + +[tool.pylint.logging] +# The type of string formatting that logging methods do. `old` means using % +# formatting, `new` is for `{}` formatting. +logging-format-style = "old" + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules = ["logging"] + +[tool.pylint."messages control"] +# Only show warnings with the listed confidence levels. Leave empty to show all. +# Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, UNDEFINED. +confidence = ["HIGH", "CONTROL_FLOW", "INFERENCE", "INFERENCE_FAILURE", "UNDEFINED"] + +# Disable the message, report, category or checker with the given id(s). You can +# either give multiple identifiers separated by comma (,) or put this option +# multiple times (only on the command line, not in the configuration file where +# it should appear only once). You can also use "--disable=all" to disable +# everything first and then re-enable specific checks. For example, if you want +# to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +disable = ["raw-checker-failed", "bad-inline-option", "locally-disabled", "file-ignored", "suppressed-message", "useless-suppression", "deprecated-pragma", "use-symbolic-message-instead", "use-implicit-booleaness-not-comparison-to-string", "use-implicit-booleaness-not-comparison-to-zero"] + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where it +# should appear only once). See also the "--disable" option for examples. +# enable = + +[tool.pylint.method_args] +# List of qualified names (i.e., library.method) which require a timeout +# parameter e.g. 'requests.api.get,requests.api.post' +timeout-methods = ["requests.api.delete", "requests.api.get", "requests.api.head", "requests.api.options", "requests.api.patch", "requests.api.post", "requests.api.put", "requests.api.request"] + +[tool.pylint.miscellaneous] +# List of note tags to take in consideration, separated by a comma. +notes = ["FIXME", "XXX", "TODO"] + +# Regular expression of note tags to take in consideration. +# notes-rgx = + +[tool.pylint.refactoring] +# Maximum number of nested blocks for function / method body +max-nested-blocks = 5 + +# Complete name of functions that never returns. When checking for inconsistent- +# return-statements if a never returning function is called then it will be +# considered as an explicit return statement and no message will be printed. +never-returning-functions = ["sys.exit", "argparse.parse_error"] + +[tool.pylint.reports] +# Python expression which should return a score less than or equal to 10. You +# have access to the variables 'fatal', 'error', 'warning', 'refactor', +# 'convention', and 'info' which contain the number of messages in each category, +# as well as 'statement' which is the total number of statements analyzed. This +# score is used by the global evaluation report (RP0004). +evaluation = "max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10))" + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details. +# msg-template = + +# Set the output format. Available formats are: text, parseable, colorized, json2 +# (improved json format), json (old json format) and msvs (visual studio). You +# can also give a reporter class, e.g. mypackage.mymodule.MyReporterClass. +# output-format = + +# Tells whether to display a full report or only the messages. +# reports = + +# Activate the evaluation score. +score = true + +[tool.pylint.similarities] +# Comments are removed from the similarity computation +ignore-comments = true + +# Docstrings are removed from the similarity computation +ignore-docstrings = true + +# Imports are removed from the similarity computation +ignore-imports = true + +# Signatures are removed from the similarity computation +ignore-signatures = true + +# Minimum lines number of a similarity. +min-similarity-lines = 4 + +[tool.pylint.spelling] +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions = 4 + +# Spelling dictionary name. No available dictionaries : You need to install both +# the python package and the system dependency for enchant to work. +# spelling-dict = + +# List of comma separated words that should be considered directives if they +# appear at the beginning of a comment and should not be checked. +spelling-ignore-comment-directives = "fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:" + +# List of comma separated words that should not be checked. +# spelling-ignore-words = + +# A path to a file that contains the private dictionary; one word per line. +# spelling-private-dict-file = + +# Tells whether to store unknown words to the private dictionary (see the +# --spelling-private-dict-file option) instead of raising a message. +# spelling-store-unknown-words = + +[tool.pylint.typecheck] +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators = ["contextlib.contextmanager"] + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +# generated-members = + +# Tells whether missing members accessed in mixin class should be ignored. A +# class is considered mixin if its name matches the mixin-class-rgx option. +# Tells whether to warn about missing members when the owner of the attribute is +# inferred to be None. +ignore-none = true + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference can +# return multiple potential results while evaluating a Python object, but some +# branches might not be evaluated, which results in partial inference. In that +# case, it might be useful to still emit no-member and other checks for the rest +# of the inferred objects. +ignore-on-opaque-inference = true + +# List of symbolic message names to ignore for Mixin members. +ignored-checks-for-mixins = ["no-member", "not-async-context-manager", "not-context-manager", "attribute-defined-outside-init"] + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes = ["optparse.Values", "thread._local", "_thread._local", "argparse.Namespace"] + +# Show a hint with possible names when a member name was not found. The aspect of +# finding the hint is based on edit distance. +missing-member-hint = true + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance = 1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices = 1 + +# Regex pattern to define which classes are considered mixins. +mixin-class-rgx = ".*[Mm]ixin" + +# List of decorators that change the signature of a decorated function. +# signature-mutators = + +[tool.pylint.variables] +# List of additional names supposed to be defined in builtins. Remember that you +# should avoid defining new builtins when possible. +# additional-builtins = + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables = true + +# List of names allowed to shadow builtins +# allowed-redefined-builtins = + +# List of strings which can identify a callback function by name. A callback name +# must start or end with one of those strings. +callbacks = ["cb_", "_cb"] + +# A regular expression matching the name of dummy variables (i.e. expected to not +# be used). +dummy-variables-rgx = "_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_" + +# Argument names that match this expression will be ignored. +ignored-argument-names = "_.*|^ignored_|^unused_" + +# Tells whether we should check for unused import in __init__ files. +# init-import = + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules = ["six.moves", "past.builtins", "future.builtins", "builtins", "io"] \ No newline at end of file diff --git a/.releaserc b/.releaserc new file mode 100644 index 0000000..bf47fdf --- /dev/null +++ b/.releaserc @@ -0,0 +1,11 @@ +{ + branches: [ + { name: "master" }, + { name: "next", channel: "next", prerelease: "rc" } + ], + plugins: [ + '@semantic-release/commit-analyzer', + '@semantic-release/release-notes-generator', + '@semantic-release/github' + ] +} diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 0000000..3a29484 --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,16 @@ +export default { + extends: ["@commitlint/config-angular"], + rules: { + "scope-empty": [2, "never"], + "subject-empty": [2, "never"], + "subject-max-length": [0], + "body-leading-blank": [0], + "footer-leading-blank": [0], + "header-max-length": [0], + "scope-case": [0], + "subject-case": [0], + "subject-full-stop": [0], + "type-case": [0], + "type-empty": [0], + }, +} diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..8240312 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,3 @@ +[mypy] +strict = True +files = src/ \ No newline at end of file From ef059ffd56844c5284db3572730d7a318b115e2d Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Tue, 9 Sep 2025 12:19:42 +0200 Subject: [PATCH 03/43] feat(folderPath): add github folder From 36ad0a7c8875c627910fe63d882b99b9b16aabb8 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Tue, 9 Sep 2025 13:52:01 +0200 Subject: [PATCH 04/43] README, dependencies, pyproject --- README.md | 32 ++++++++++++++++++++++++++++++++ app.py | 0 pyproject.toml | 36 ++++++++++++++++++++++++++++++++++++ requirements.in | 0 requirements.txt | 11 +++++++++++ 5 files changed, 79 insertions(+) create mode 100644 README.md create mode 100644 app.py create mode 100644 pyproject.toml create mode 100644 requirements.in create mode 100644 requirements.txt diff --git a/README.md b/README.md new file mode 100644 index 0000000..dae381c --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +

OpenGeodeWeb-Backby Geode-solutions

+

OpenSource Python framework based on OpenGeode

+ +

+ Build Status + Deploy Status + Coverage Status + Version + PyPI +

+ +

+ Language + License + Semantic-release +

+ +--- + +## Introduction + +OpenGeodeWeb-Microservice is a dedicated service that manages the database logic extracted from the OpenGeodeWeb-Back (OGWB) project. It provides a REST API for managing geological data and associated metadata. + +## Changelog + +Detailed changes for each release are documented in the [release notes](https://github.com/Geode-solutions/OpenGeodeWeb-Microservice/releases). + +## License + +[MIT](https://opensource.org/licenses/MIT) + +Copyright (c) 2019 - 2025, Geode-solutions diff --git a/app.py b/app.py new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e4da16f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,36 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[project] +name = "OpenGeodeWeb-Microservice" +version = "1.0.0" +dynamic = ["dependencies"] +authors = [{ name = "Geode-solutions", email = "team-web@geode-solutions.com" }] +description = "Microservice Python pour la gestion des données et de la base de données de l'écosystème OpenGeodeWeb" +readme = "README.md" +requires-python = ">=3.9, <3.13" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Framework :: Flask", + "Topic :: Database", + "Topic :: Scientific/Engineering", +] + +[project.urls] +"Homepage" = "https://github.com/Geode-solutions/OpenGeodeWeb-Microservice" +"Bug Tracker" = "https://github.com/Geode-solutions/OpenGeodeWeb-Microservice/issues" +"Repository" = "https://github.com/Geode-solutions/OpenGeodeWeb-Microservice.git" + +[tool.setuptools.dynamic] +dependencies = { file = ["requirements.txt"] } + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.setuptools.package-data] +"ogwm" = ["*.json"] +"ogwm.routes" = ["*.json"] +"ogwm.models" = ["*.json"] diff --git a/requirements.in b/requirements.in new file mode 100644 index 0000000..e69de29 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..327208f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,11 @@ +# Flask et extensions +Flask[async]==3.0.3 +Flask-Cors==6.0.1 +Flask-SQLAlchemy==3.1.1 +werkzeug==3.0.3 + +# Base de données et validation +fastjsonschema==2.16.2 + +# Utilitaires +python-dotenv==1.0.0 \ No newline at end of file From e9c1d221c0b7e0d8e48eecf1d176ef7eb7d89530 Mon Sep 17 00:00:00 2001 From: MaxNumerique <144453705+MaxNumerique@users.noreply.github.com> Date: Tue, 9 Sep 2025 11:52:22 +0000 Subject: [PATCH 05/43] Apply prepare changes --- requirements.txt | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/requirements.txt b/requirements.txt index 327208f..7c76e44 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,6 @@ -# Flask et extensions -Flask[async]==3.0.3 -Flask-Cors==6.0.1 -Flask-SQLAlchemy==3.1.1 -werkzeug==3.0.3 - -# Base de données et validation -fastjsonschema==2.16.2 - -# Utilitaires -python-dotenv==1.0.0 \ No newline at end of file +# +# This file is autogenerated by pip-compile with Python 3.12 +# by the following command: +# +# pip-compile --pre requirements.in +# From 8aa474821ff8b2b4081b3cf0040c558079005d6c Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Tue, 9 Sep 2025 17:02:15 +0200 Subject: [PATCH 06/43] gitgnore added requirements.in added config added app.config and conftest added temporarily connection to database model of database pyproject.toml READEME.md added requirements.txt added --- .gitignore | 6 +++ README.md | 2 +- pyproject.toml | 11 +---- requirements.txt | 13 +----- .../opengeodeweb_microservice/__init__.py | 0 src/opengeodeweb_microservice/app_config.py | 38 ++++++++++++++++ .../database/__init__.py | 0 .../database/connection.py | 28 ++++++++++++ .../database/session.py | 22 +++++++++ .../microservice/__init__.py | 0 .../microservice/base.py | 19 ++++++++ .../microservice/data.py | 45 +++++++++++++++++++ tests/__init__.py | 0 tests/conftest.py | 35 +++++++++++++++ tests/test_database.py | 13 ++++++ 15 files changed, 210 insertions(+), 22 deletions(-) create mode 100644 .gitignore rename app.py => src/opengeodeweb_microservice/__init__.py (100%) create mode 100644 src/opengeodeweb_microservice/app_config.py rename requirements.in => src/opengeodeweb_microservice/database/__init__.py (100%) create mode 100644 src/opengeodeweb_microservice/database/connection.py create mode 100644 src/opengeodeweb_microservice/database/session.py create mode 100644 src/opengeodeweb_microservice/microservice/__init__.py create mode 100644 src/opengeodeweb_microservice/microservice/base.py create mode 100644 src/opengeodeweb_microservice/microservice/data.py create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/test_database.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..156cf3f --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.pytest_cache +venv +__pycache__ +node_modules +.mypy_cache +*.egg-info \ No newline at end of file diff --git a/README.md b/README.md index dae381c..5bde620 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ ## Introduction -OpenGeodeWeb-Microservice is a dedicated service that manages the database logic extracted from the OpenGeodeWeb-Back (OGWB) project. It provides a REST API for managing geological data and associated metadata. +Database model and ORM layer for the OpenGeodeWeb ecosystem. ## Changelog diff --git a/pyproject.toml b/pyproject.toml index e4da16f..5ca2442 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,30 +7,21 @@ name = "OpenGeodeWeb-Microservice" version = "1.0.0" dynamic = ["dependencies"] authors = [{ name = "Geode-solutions", email = "team-web@geode-solutions.com" }] -description = "Microservice Python pour la gestion des données et de la base de données de l'écosystème OpenGeodeWeb" +description = "Database model and ORM layer for OpenGeodeWeb ecosystem" readme = "README.md" requires-python = ">=3.9, <3.13" classifiers = [ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", - "Framework :: Flask", - "Topic :: Database", - "Topic :: Scientific/Engineering", ] [project.urls] "Homepage" = "https://github.com/Geode-solutions/OpenGeodeWeb-Microservice" "Bug Tracker" = "https://github.com/Geode-solutions/OpenGeodeWeb-Microservice/issues" -"Repository" = "https://github.com/Geode-solutions/OpenGeodeWeb-Microservice.git" [tool.setuptools.dynamic] dependencies = { file = ["requirements.txt"] } [tool.setuptools.packages.find] where = ["src"] - -[tool.setuptools.package-data] -"ogwm" = ["*.json"] -"ogwm.routes" = ["*.json"] -"ogwm.models" = ["*.json"] diff --git a/requirements.txt b/requirements.txt index 327208f..7a9f75b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,2 @@ -# Flask et extensions -Flask[async]==3.0.3 -Flask-Cors==6.0.1 -Flask-SQLAlchemy==3.1.1 -werkzeug==3.0.3 - -# Base de données et validation -fastjsonschema==2.16.2 - -# Utilitaires -python-dotenv==1.0.0 \ No newline at end of file +SQLAlchemy>=2.0.0 +Flask-SQLAlchemy>=3.1.1 \ No newline at end of file diff --git a/app.py b/src/opengeodeweb_microservice/__init__.py similarity index 100% rename from app.py rename to src/opengeodeweb_microservice/__init__.py diff --git a/src/opengeodeweb_microservice/app_config.py b/src/opengeodeweb_microservice/app_config.py new file mode 100644 index 0000000..dd5d06d --- /dev/null +++ b/src/opengeodeweb_microservice/app_config.py @@ -0,0 +1,38 @@ +# # Standard library imports +# import os +# import time + +# # Third party imports +# # Local application imports +# from .database import DATABASE_FILENAME + + +# class Config(object): +# FLASK_DEBUG = os.environ.get("FLASK_DEBUG", default=False) +# DEFAULT_HOST = "localhost" +# DEFAULT_PORT = "5000" +# CORS_HEADERS = "Content-Type" +# UPLOAD_FOLDER = "./uploads" +# REQUEST_COUNTER = 0 +# LAST_REQUEST_TIME = time.time() +# LAST_PING_TIME = time.time() +# SQLALCHEMY_TRACK_MODIFICATIONS = False + + +# class ProdConfig(Config): +# DATA_FOLDER_PATH = "/data" +# SQLALCHEMY_DATABASE_URI = f"sqlite:///{os.path.abspath( +# os.path.join(DATA_FOLDER_PATH, DATABASE_FILENAME) +# )}" + + +# class DevConfig(Config): +# SSL = None +# ORIGINS = "*" +# MINUTES_BEFORE_TIMEOUT = "1" +# SECONDS_BETWEEN_SHUTDOWNS = "10" +# BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +# DATA_FOLDER_PATH = os.path.join(BASE_DIR, "data") +# SQLALCHEMY_DATABASE_URI = f"sqlite:///{os.path.join( +# BASE_DIR, DATA_FOLDER_PATH, DATABASE_FILENAME +# )}" diff --git a/requirements.in b/src/opengeodeweb_microservice/database/__init__.py similarity index 100% rename from requirements.in rename to src/opengeodeweb_microservice/database/__init__.py diff --git a/src/opengeodeweb_microservice/database/connection.py b/src/opengeodeweb_microservice/database/connection.py new file mode 100644 index 0000000..8751d92 --- /dev/null +++ b/src/opengeodeweb_microservice/database/connection.py @@ -0,0 +1,28 @@ +"""Database connection management""" + +from typing import Optional +from flask import Flask +from flask_sqlalchemy import SQLAlchemy + +from src.opengeodeweb_microservice.microservice.base import Base + + +class DatabaseConnection: + + def __init__(self, database_filename: str = "project.db"): + self.database_filename = database_filename + self.database: Optional[SQLAlchemy] = None + + def init_app(self, app: Flask) -> SQLAlchemy: + if self.database is None: + self.database = SQLAlchemy(model_class=Base) + + self.database.init_app(app) + + with app.app_context(): + self.database.create_all() + + return self.database + + def get_database(self) -> Optional[SQLAlchemy]: + return self.database diff --git a/src/opengeodeweb_microservice/database/session.py b/src/opengeodeweb_microservice/database/session.py new file mode 100644 index 0000000..4b4240d --- /dev/null +++ b/src/opengeodeweb_microservice/database/session.py @@ -0,0 +1,22 @@ +from typing import Optional +from flask import Flask +from flask_sqlalchemy import SQLAlchemy + +from src.opengeodeweb_microservice.database.connection import DatabaseConnection + +# Global database instance +_database_connection = DatabaseConnection() + + +def init_database(app: Flask, database_filename: str = "project.db") -> SQLAlchemy: + global _database_connection + _database_connection.database_filename = database_filename + return _database_connection.init_app(app) + + +def get_session() -> Optional[SQLAlchemy]: + return _database_connection.get_database() + + +def get_database_connection() -> DatabaseConnection: + return _database_connection diff --git a/src/opengeodeweb_microservice/microservice/__init__.py b/src/opengeodeweb_microservice/microservice/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/opengeodeweb_microservice/microservice/base.py b/src/opengeodeweb_microservice/microservice/base.py new file mode 100644 index 0000000..d5e2f0f --- /dev/null +++ b/src/opengeodeweb_microservice/microservice/base.py @@ -0,0 +1,19 @@ +from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from sqlalchemy.orm import DeclarativeBase + +DATABASE_FILENAME = "project.db" + + +class Base(DeclarativeBase): + pass + + +database = SQLAlchemy(model_class=Base) + + +def initialize_database(app: Flask) -> SQLAlchemy: + database.init_app(app) + with app.app_context(): + database.create_all() + return database diff --git a/src/opengeodeweb_microservice/microservice/data.py b/src/opengeodeweb_microservice/microservice/data.py new file mode 100644 index 0000000..d033c1c --- /dev/null +++ b/src/opengeodeweb_microservice/microservice/data.py @@ -0,0 +1,45 @@ +from sqlalchemy import String, JSON +from sqlalchemy.orm import Mapped, mapped_column +from src.opengeodeweb_microservice.microservice.base import database, Base +import uuid + + +class Data(Base): + __tablename__ = "datas" + + id: Mapped[str] = mapped_column( + String, primary_key=True, default=lambda: str(uuid.uuid4()).replace("-", "") + ) + native_file_name: Mapped[str] = mapped_column(String, nullable=False) + viewable_file_name: Mapped[str] = mapped_column(String, nullable=False) + geode_object: Mapped[str] = mapped_column(String, nullable=False) + + light_viewable: Mapped[str | None] = mapped_column(String, nullable=True) + input_file: Mapped[str | None] = mapped_column(String, nullable=True) + additional_files: Mapped[list[str] | None] = mapped_column(JSON, nullable=True) + + @staticmethod + def create( + geode_object: str, + input_file: str | None = None, + additional_files: list[str] | None = None, + ) -> "Data": + input_file = input_file if input_file is not None else "" + additional_files = additional_files if additional_files is not None else [] + + data_entry = Data( + geode_object=geode_object, + input_file=input_file, + additional_files=additional_files, + native_file_name="", + viewable_file_name="", + light_viewable=None, + ) + + database.session.add(data_entry) + database.session.flush() + return data_entry + + @staticmethod + def get(data_id: str) -> "Data | None": + return database.session.get(Data, data_id) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..c23169d --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,35 @@ +# # Standard library imports +# import time +# import shutil + +# # Third party imports +# import os +# import pytest + +# # Local application imports +# from app import app +# from src.opengeodeweb_microservice.database import initialize_database + + +# @pytest.fixture(scope="session", autouse=True) +# def copy_data(): +# app.config["DATA_FOLDER_PATH"] = "./data/" +# BASE_DIR = os.path.abspath(os.path.dirname(__file__)) +# db_path = os.path.join(BASE_DIR, "data", "project.db") +# app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{db_path}" +# initialize_database(app) + + +# @pytest.fixture +# def client(): +# app.config["REQUEST_COUNTER"] = 0 +# app.config["LAST_REQUEST_TIME"] = time.time() +# client = app.test_client() +# client.headers = {"Content-type": "application/json", "Accept": "application/json"} +# yield client + + +# @pytest.fixture +# def app_context(): +# with app.app_context(): +# yield diff --git a/tests/test_database.py b/tests/test_database.py new file mode 100644 index 0000000..f7c70f7 --- /dev/null +++ b/tests/test_database.py @@ -0,0 +1,13 @@ +# import os + + +# def test_database_uri_path(client): +# app = client.application +# with app.app_context(): +# base_dir = os.path.abspath(os.path.dirname(__file__)) +# expected_db_path = os.path.join(base_dir, "data", "project.db") +# expected_uri = f"sqlite:///{expected_db_path}" + +# assert app.config["SQLALCHEMY_DATABASE_URI"] == expected_uri + +# assert os.path.exists(expected_db_path) From 8775ad263986043d739080b36627acf0e17962bc Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 10 Sep 2025 09:28:04 +0200 Subject: [PATCH 07/43] app_config.py added import session into data.py --- src/opengeodeweb_microservice/app_config.py | 47 +++++-------------- .../microservice/data.py | 11 +++-- 2 files changed, 20 insertions(+), 38 deletions(-) diff --git a/src/opengeodeweb_microservice/app_config.py b/src/opengeodeweb_microservice/app_config.py index dd5d06d..d7006aa 100644 --- a/src/opengeodeweb_microservice/app_config.py +++ b/src/opengeodeweb_microservice/app_config.py @@ -1,38 +1,17 @@ -# # Standard library imports -# import os -# import time +import os +from .database.session import DATABASE_FILENAME -# # Third party imports -# # Local application imports -# from .database import DATABASE_FILENAME +class ProdConfig(Config): + DATA_FOLDER_PATH = "/data" + SQLALCHEMY_DATABASE_URI = f"sqlite:///{os.path.abspath( + os.path.join(DATA_FOLDER_PATH, DATABASE_FILENAME) + )}" -# class Config(object): -# FLASK_DEBUG = os.environ.get("FLASK_DEBUG", default=False) -# DEFAULT_HOST = "localhost" -# DEFAULT_PORT = "5000" -# CORS_HEADERS = "Content-Type" -# UPLOAD_FOLDER = "./uploads" -# REQUEST_COUNTER = 0 -# LAST_REQUEST_TIME = time.time() -# LAST_PING_TIME = time.time() -# SQLALCHEMY_TRACK_MODIFICATIONS = False - -# class ProdConfig(Config): -# DATA_FOLDER_PATH = "/data" -# SQLALCHEMY_DATABASE_URI = f"sqlite:///{os.path.abspath( -# os.path.join(DATA_FOLDER_PATH, DATABASE_FILENAME) -# )}" - - -# class DevConfig(Config): -# SSL = None -# ORIGINS = "*" -# MINUTES_BEFORE_TIMEOUT = "1" -# SECONDS_BETWEEN_SHUTDOWNS = "10" -# BASE_DIR = os.path.dirname(os.path.abspath(__file__)) -# DATA_FOLDER_PATH = os.path.join(BASE_DIR, "data") -# SQLALCHEMY_DATABASE_URI = f"sqlite:///{os.path.join( -# BASE_DIR, DATA_FOLDER_PATH, DATABASE_FILENAME -# )}" +class DevConfig(Config): + BASE_DIR = os.path.dirname(os.path.abspath(__file__)) + DATA_FOLDER_PATH = os.path.join(BASE_DIR, "data") + SQLALCHEMY_DATABASE_URI = f"sqlite:///{os.path.join( + BASE_DIR, DATA_FOLDER_PATH, DATABASE_FILENAME + )}" diff --git a/src/opengeodeweb_microservice/microservice/data.py b/src/opengeodeweb_microservice/microservice/data.py index d033c1c..5dbf691 100644 --- a/src/opengeodeweb_microservice/microservice/data.py +++ b/src/opengeodeweb_microservice/microservice/data.py @@ -1,6 +1,7 @@ from sqlalchemy import String, JSON from sqlalchemy.orm import Mapped, mapped_column -from src.opengeodeweb_microservice.microservice.base import database, Base +from ..database.session import get_session +from .base import Base import uuid @@ -36,10 +37,12 @@ def create( light_viewable=None, ) - database.session.add(data_entry) - database.session.flush() + session = get_session() + session.session.add(data_entry) + session.session.flush() return data_entry @staticmethod def get(data_id: str) -> "Data | None": - return database.session.get(Data, data_id) + session = get_session() + return session.session.get(Data, data_id) From 288375874371809710bfb44834bf334efd06a590 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 10 Sep 2025 11:46:45 +0200 Subject: [PATCH 08/43] tests added gitignore updated --- .gitignore | 3 +- .../database/session.py | 1 - tests/conftest.py | 84 +++++++++++-------- tests/test_connection.py | 42 ++++++++++ tests/test_database.py | 47 +++++++++-- 5 files changed, 131 insertions(+), 46 deletions(-) create mode 100644 tests/test_connection.py diff --git a/.gitignore b/.gitignore index 156cf3f..11aedd1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .pytest_cache +*.egg-info venv __pycache__ node_modules .mypy_cache -*.egg-info \ No newline at end of file +*.db \ No newline at end of file diff --git a/src/opengeodeweb_microservice/database/session.py b/src/opengeodeweb_microservice/database/session.py index 4b4240d..b1d3bdc 100644 --- a/src/opengeodeweb_microservice/database/session.py +++ b/src/opengeodeweb_microservice/database/session.py @@ -4,7 +4,6 @@ from src.opengeodeweb_microservice.database.connection import DatabaseConnection -# Global database instance _database_connection = DatabaseConnection() diff --git a/tests/conftest.py b/tests/conftest.py index c23169d..d6b58b5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,35 +1,49 @@ -# # Standard library imports -# import time -# import shutil - -# # Third party imports -# import os -# import pytest - -# # Local application imports -# from app import app -# from src.opengeodeweb_microservice.database import initialize_database - - -# @pytest.fixture(scope="session", autouse=True) -# def copy_data(): -# app.config["DATA_FOLDER_PATH"] = "./data/" -# BASE_DIR = os.path.abspath(os.path.dirname(__file__)) -# db_path = os.path.join(BASE_DIR, "data", "project.db") -# app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{db_path}" -# initialize_database(app) - - -# @pytest.fixture -# def client(): -# app.config["REQUEST_COUNTER"] = 0 -# app.config["LAST_REQUEST_TIME"] = time.time() -# client = app.test_client() -# client.headers = {"Content-type": "application/json", "Accept": "application/json"} -# yield client - - -# @pytest.fixture -# def app_context(): -# with app.app_context(): -# yield +import os +import pytest +from flask import Flask +from src.opengeodeweb_microservice.database.session import init_database, get_session +from src.opengeodeweb_microservice.microservice.data import Data + + +@pytest.fixture(scope="session") +def app(): + app = Flask(__name__) + app.config["TESTING"] = True + + BASE_DIR = os.path.abspath(os.path.dirname(__file__)) + db_path = os.path.join(BASE_DIR, "test_project.db") + app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{db_path}" + + with app.app_context(): + db = init_database(app, "test_project.db") + yield app + try: + session = get_session() + if session: + session.session.close() + if hasattr(session, 'engine'): + session.engine.dispose() + except Exception: + pass + import time + time.sleep(0.1) + if os.path.exists(db_path): + try: + os.remove(db_path) + except PermissionError: + pass + + +@pytest.fixture +def app_context(app): + with app.app_context(): + yield + + +@pytest.fixture +def clean_database(app_context): + session = get_session() + if session: + session.session.query(Data).delete() + session.session.commit() + yield diff --git a/tests/test_connection.py b/tests/test_connection.py new file mode 100644 index 0000000..e59a09b --- /dev/null +++ b/tests/test_connection.py @@ -0,0 +1,42 @@ +import pytest +from src.opengeodeweb_microservice.database.session import ( + get_session, + get_database_connection +) +from src.opengeodeweb_microservice.database.connection import DatabaseConnection +from src.opengeodeweb_microservice.microservice.data import Data + + +def test_database_connection_basic(app_context): + session = get_session() + assert session is not None + assert session.session is not None + connection = get_database_connection() + assert connection is not None + + +def test_data_crud_operations(app_context, clean_database): + data = Data.create( + geode_object="test_object", + input_file="test.txt" + ) + assert data.id is not None + session = get_session() + session.session.commit() + retrieved = Data.get(data.id) + assert retrieved is not None + assert retrieved.geode_object == "test_object" + non_existent = Data.get("fake_id") + assert non_existent is None + + +def test_data_with_additional_files(app_context, clean_database): + files = ["file1.txt", "file2.txt"] + data = Data.create( + geode_object="test_files", + additional_files=files + ) + session = get_session() + session.session.commit() + retrieved = Data.get(data.id) + assert retrieved.additional_files == files \ No newline at end of file diff --git a/tests/test_database.py b/tests/test_database.py index f7c70f7..27eb24b 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -1,13 +1,42 @@ -# import os +import pytest +from src.opengeodeweb_microservice.database.session import get_session, get_database_connection +from src.opengeodeweb_microservice.microservice.data import Data -# def test_database_uri_path(client): -# app = client.application -# with app.app_context(): -# base_dir = os.path.abspath(os.path.dirname(__file__)) -# expected_db_path = os.path.join(base_dir, "data", "project.db") -# expected_uri = f"sqlite:///{expected_db_path}" +def test_database_connection(app_context): + session = get_session() + assert session is not None + assert session.session is not None + connection = get_database_connection() + assert connection is not None -# assert app.config["SQLALCHEMY_DATABASE_URI"] == expected_uri -# assert os.path.exists(expected_db_path) +def test_data_creation_and_retrieval(app_context, clean_database): + data = Data.create( + geode_object="test_object", + input_file="test.txt" + ) + assert data.id is not None + assert data.geode_object == "test_object" + session = get_session() + session.session.commit() + retrieved = Data.get(data.id) + assert retrieved is not None + assert retrieved.geode_object == "test_object" + + +def test_data_with_additional_files(app_context, clean_database): + files = ["file1.txt", "file2.txt"] + data = Data.create( + geode_object="test_with_files", + additional_files=files + ) + session = get_session() + session.session.commit() + retrieved = Data.get(data.id) + assert retrieved.additional_files == files + + +def test_data_get_nonexistent(app_context): + result = Data.get("nonexistent_id") + assert result is None \ No newline at end of file From 786c0ed236dab0b065ddd637afdf6d78a175c9fb Mon Sep 17 00:00:00 2001 From: MaxNumerique <144453705+MaxNumerique@users.noreply.github.com> Date: Wed, 10 Sep 2025 09:47:01 +0000 Subject: [PATCH 09/43] Apply prepare changes --- tests/conftest.py | 7 ++++--- tests/test_connection.py | 16 +++++----------- tests/test_database.py | 17 +++++++---------- 3 files changed, 16 insertions(+), 24 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index d6b58b5..23d653e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,11 +9,11 @@ def app(): app = Flask(__name__) app.config["TESTING"] = True - + BASE_DIR = os.path.abspath(os.path.dirname(__file__)) db_path = os.path.join(BASE_DIR, "test_project.db") app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{db_path}" - + with app.app_context(): db = init_database(app, "test_project.db") yield app @@ -21,11 +21,12 @@ def app(): session = get_session() if session: session.session.close() - if hasattr(session, 'engine'): + if hasattr(session, "engine"): session.engine.dispose() except Exception: pass import time + time.sleep(0.1) if os.path.exists(db_path): try: diff --git a/tests/test_connection.py b/tests/test_connection.py index e59a09b..ed1595b 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1,7 +1,7 @@ import pytest from src.opengeodeweb_microservice.database.session import ( - get_session, - get_database_connection + get_session, + get_database_connection, ) from src.opengeodeweb_microservice.database.connection import DatabaseConnection from src.opengeodeweb_microservice.microservice.data import Data @@ -16,10 +16,7 @@ def test_database_connection_basic(app_context): def test_data_crud_operations(app_context, clean_database): - data = Data.create( - geode_object="test_object", - input_file="test.txt" - ) + data = Data.create(geode_object="test_object", input_file="test.txt") assert data.id is not None session = get_session() session.session.commit() @@ -32,11 +29,8 @@ def test_data_crud_operations(app_context, clean_database): def test_data_with_additional_files(app_context, clean_database): files = ["file1.txt", "file2.txt"] - data = Data.create( - geode_object="test_files", - additional_files=files - ) + data = Data.create(geode_object="test_files", additional_files=files) session = get_session() session.session.commit() retrieved = Data.get(data.id) - assert retrieved.additional_files == files \ No newline at end of file + assert retrieved.additional_files == files diff --git a/tests/test_database.py b/tests/test_database.py index 27eb24b..66ae2e4 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -1,5 +1,8 @@ import pytest -from src.opengeodeweb_microservice.database.session import get_session, get_database_connection +from src.opengeodeweb_microservice.database.session import ( + get_session, + get_database_connection, +) from src.opengeodeweb_microservice.microservice.data import Data @@ -12,10 +15,7 @@ def test_database_connection(app_context): def test_data_creation_and_retrieval(app_context, clean_database): - data = Data.create( - geode_object="test_object", - input_file="test.txt" - ) + data = Data.create(geode_object="test_object", input_file="test.txt") assert data.id is not None assert data.geode_object == "test_object" session = get_session() @@ -27,10 +27,7 @@ def test_data_creation_and_retrieval(app_context, clean_database): def test_data_with_additional_files(app_context, clean_database): files = ["file1.txt", "file2.txt"] - data = Data.create( - geode_object="test_with_files", - additional_files=files - ) + data = Data.create(geode_object="test_with_files", additional_files=files) session = get_session() session.session.commit() retrieved = Data.get(data.id) @@ -39,4 +36,4 @@ def test_data_with_additional_files(app_context, clean_database): def test_data_get_nonexistent(app_context): result = Data.get("nonexistent_id") - assert result is None \ No newline at end of file + assert result is None From d5cdae49db657e2060135fd8f1651fb8ed78b394 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 10 Sep 2025 11:54:37 +0200 Subject: [PATCH 10/43] simplified test, session context, dataabase initialization, conftest --- .../database/session.py | 6 +--- .../microservice/base.py | 12 ------- tests/conftest.py | 36 ++++++++++++------- tests/test_database.py | 34 +++++------------- 4 files changed, 33 insertions(+), 55 deletions(-) diff --git a/src/opengeodeweb_microservice/database/session.py b/src/opengeodeweb_microservice/database/session.py index b1d3bdc..2ee475b 100644 --- a/src/opengeodeweb_microservice/database/session.py +++ b/src/opengeodeweb_microservice/database/session.py @@ -1,21 +1,17 @@ from typing import Optional from flask import Flask from flask_sqlalchemy import SQLAlchemy - -from src.opengeodeweb_microservice.database.connection import DatabaseConnection +from .connection import DatabaseConnection _database_connection = DatabaseConnection() - def init_database(app: Flask, database_filename: str = "project.db") -> SQLAlchemy: global _database_connection _database_connection.database_filename = database_filename return _database_connection.init_app(app) - def get_session() -> Optional[SQLAlchemy]: return _database_connection.get_database() - def get_database_connection() -> DatabaseConnection: return _database_connection diff --git a/src/opengeodeweb_microservice/microservice/base.py b/src/opengeodeweb_microservice/microservice/base.py index d5e2f0f..8bb12e6 100644 --- a/src/opengeodeweb_microservice/microservice/base.py +++ b/src/opengeodeweb_microservice/microservice/base.py @@ -2,18 +2,6 @@ from flask_sqlalchemy import SQLAlchemy from sqlalchemy.orm import DeclarativeBase -DATABASE_FILENAME = "project.db" - class Base(DeclarativeBase): pass - - -database = SQLAlchemy(model_class=Base) - - -def initialize_database(app: Flask) -> SQLAlchemy: - database.init_app(app) - with app.app_context(): - database.create_all() - return database diff --git a/tests/conftest.py b/tests/conftest.py index d6b58b5..bd4249e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,24 +9,29 @@ def app(): app = Flask(__name__) app.config["TESTING"] = True - BASE_DIR = os.path.abspath(os.path.dirname(__file__)) db_path = os.path.join(BASE_DIR, "test_project.db") app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{db_path}" - + app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False with app.app_context(): - db = init_database(app, "test_project.db") + init_database(app, "test_project.db") yield app - try: - session = get_session() - if session: - session.session.close() - if hasattr(session, 'engine'): - session.engine.dispose() - except Exception: - pass - import time - time.sleep(0.1) + _cleanup_database_connections() + _remove_test_database(db_path) + + +def _cleanup_database_connections(): + try: + session = get_session() + if session and hasattr(session, 'session'): + session.session.close() + if session and hasattr(session, 'engine'): + session.engine.dispose() + except Exception: + pass + + +def _remove_test_database(db_path: str): if os.path.exists(db_path): try: os.remove(db_path) @@ -47,3 +52,8 @@ def clean_database(app_context): session.session.query(Data).delete() session.session.commit() yield + if session: + try: + session.session.rollback() + except Exception: + pass diff --git a/tests/test_database.py b/tests/test_database.py index 27eb24b..92a90eb 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -1,42 +1,26 @@ import pytest -from src.opengeodeweb_microservice.database.session import get_session, get_database_connection +from src.opengeodeweb_microservice.database.session import get_session from src.opengeodeweb_microservice.microservice.data import Data - -def test_database_connection(app_context): +def test_database_session_available(app_context): session = get_session() assert session is not None assert session.session is not None - connection = get_database_connection() - assert connection is not None - -def test_data_creation_and_retrieval(app_context, clean_database): +def test_data_crud_operations(app_context, clean_database): data = Data.create( geode_object="test_object", - input_file="test.txt" + input_file="test.txt", + additional_files=["file1.txt", "file2.txt"] ) assert data.id is not None assert data.geode_object == "test_object" + assert data.additional_files == ["file1.txt", "file2.txt"] session = get_session() session.session.commit() retrieved = Data.get(data.id) assert retrieved is not None assert retrieved.geode_object == "test_object" - - -def test_data_with_additional_files(app_context, clean_database): - files = ["file1.txt", "file2.txt"] - data = Data.create( - geode_object="test_with_files", - additional_files=files - ) - session = get_session() - session.session.commit() - retrieved = Data.get(data.id) - assert retrieved.additional_files == files - - -def test_data_get_nonexistent(app_context): - result = Data.get("nonexistent_id") - assert result is None \ No newline at end of file + assert retrieved.additional_files == ["file1.txt", "file2.txt"] + non_existent = Data.get("fake_id") + assert non_existent is None \ No newline at end of file From 73d15d66ede774272512b93e9be6db44d2c10476 Mon Sep 17 00:00:00 2001 From: MaxNumerique <144453705+MaxNumerique@users.noreply.github.com> Date: Wed, 10 Sep 2025 09:55:53 +0000 Subject: [PATCH 11/43] Apply prepare changes --- src/opengeodeweb_microservice/database/session.py | 3 +++ tests/conftest.py | 4 ++-- tests/test_database.py | 6 ++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/opengeodeweb_microservice/database/session.py b/src/opengeodeweb_microservice/database/session.py index 2ee475b..ce2dd3e 100644 --- a/src/opengeodeweb_microservice/database/session.py +++ b/src/opengeodeweb_microservice/database/session.py @@ -5,13 +5,16 @@ _database_connection = DatabaseConnection() + def init_database(app: Flask, database_filename: str = "project.db") -> SQLAlchemy: global _database_connection _database_connection.database_filename = database_filename return _database_connection.init_app(app) + def get_session() -> Optional[SQLAlchemy]: return _database_connection.get_database() + def get_database_connection() -> DatabaseConnection: return _database_connection diff --git a/tests/conftest.py b/tests/conftest.py index bd4249e..2871ed9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -23,9 +23,9 @@ def app(): def _cleanup_database_connections(): try: session = get_session() - if session and hasattr(session, 'session'): + if session and hasattr(session, "session"): session.session.close() - if session and hasattr(session, 'engine'): + if session and hasattr(session, "engine"): session.engine.dispose() except Exception: pass diff --git a/tests/test_database.py b/tests/test_database.py index 92a90eb..06ff861 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -2,16 +2,18 @@ from src.opengeodeweb_microservice.database.session import get_session from src.opengeodeweb_microservice.microservice.data import Data + def test_database_session_available(app_context): session = get_session() assert session is not None assert session.session is not None + def test_data_crud_operations(app_context, clean_database): data = Data.create( geode_object="test_object", input_file="test.txt", - additional_files=["file1.txt", "file2.txt"] + additional_files=["file1.txt", "file2.txt"], ) assert data.id is not None assert data.geode_object == "test_object" @@ -23,4 +25,4 @@ def test_data_crud_operations(app_context, clean_database): assert retrieved.geode_object == "test_object" assert retrieved.additional_files == ["file1.txt", "file2.txt"] non_existent = Data.get("fake_id") - assert non_existent is None \ No newline at end of file + assert non_existent is None From 32914580dea614f28701fd69db82408e3a009a17 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 10 Sep 2025 11:57:17 +0200 Subject: [PATCH 12/43] removed unused import in test_connection.py --- tests/test_connection.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index ed1595b..125069b 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3,7 +3,6 @@ get_session, get_database_connection, ) -from src.opengeodeweb_microservice.database.connection import DatabaseConnection from src.opengeodeweb_microservice.microservice.data import Data From 21a2f547456aba5e13c8cf336bac34663cf31b30 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 10 Sep 2025 14:16:18 +0200 Subject: [PATCH 13/43] refacto --- src/opengeodeweb_microservice/app_config.py | 4 ++- .../database/connection.py | 36 ++++++++++--------- .../database/session.py | 20 ----------- .../microservice/data.py | 12 ++++--- tests/conftest.py | 19 ++++------ tests/test_connection.py | 26 +------------- tests/test_database.py | 31 +++++++--------- 7 files changed, 48 insertions(+), 100 deletions(-) delete mode 100644 src/opengeodeweb_microservice/database/session.py diff --git a/src/opengeodeweb_microservice/app_config.py b/src/opengeodeweb_microservice/app_config.py index d7006aa..d33e8e9 100644 --- a/src/opengeodeweb_microservice/app_config.py +++ b/src/opengeodeweb_microservice/app_config.py @@ -1,6 +1,8 @@ import os -from .database.session import DATABASE_FILENAME +from .database.connection import DATABASE_FILENAME +class Config: + pass class ProdConfig(Config): DATA_FOLDER_PATH = "/data" diff --git a/src/opengeodeweb_microservice/database/connection.py b/src/opengeodeweb_microservice/database/connection.py index 8751d92..e91773c 100644 --- a/src/opengeodeweb_microservice/database/connection.py +++ b/src/opengeodeweb_microservice/database/connection.py @@ -3,26 +3,28 @@ from typing import Optional from flask import Flask from flask_sqlalchemy import SQLAlchemy +from ..microservice.base import Base -from src.opengeodeweb_microservice.microservice.base import Base +DATABASE_FILENAME = "project.db" +db: Optional[SQLAlchemy] = None -class DatabaseConnection: +def init_database(app: Flask, db_filename: str = DATABASE_FILENAME) -> SQLAlchemy: + global db + if db is None: + db = SQLAlchemy(model_class=Base) + db.init_app(app) + with app.app_context(): + db.create_all() + return db - def __init__(self, database_filename: str = "project.db"): - self.database_filename = database_filename - self.database: Optional[SQLAlchemy] = None +def get_database() -> Optional[SQLAlchemy]: + return db - def init_app(self, app: Flask) -> SQLAlchemy: - if self.database is None: - self.database = SQLAlchemy(model_class=Base) +def get_session(): + if db is None: + return None + return db.session - self.database.init_app(app) - - with app.app_context(): - self.database.create_all() - - return self.database - - def get_database(self) -> Optional[SQLAlchemy]: - return self.database +def get_database_connection(): + return get_database() diff --git a/src/opengeodeweb_microservice/database/session.py b/src/opengeodeweb_microservice/database/session.py deleted file mode 100644 index ce2dd3e..0000000 --- a/src/opengeodeweb_microservice/database/session.py +++ /dev/null @@ -1,20 +0,0 @@ -from typing import Optional -from flask import Flask -from flask_sqlalchemy import SQLAlchemy -from .connection import DatabaseConnection - -_database_connection = DatabaseConnection() - - -def init_database(app: Flask, database_filename: str = "project.db") -> SQLAlchemy: - global _database_connection - _database_connection.database_filename = database_filename - return _database_connection.init_app(app) - - -def get_session() -> Optional[SQLAlchemy]: - return _database_connection.get_database() - - -def get_database_connection() -> DatabaseConnection: - return _database_connection diff --git a/src/opengeodeweb_microservice/microservice/data.py b/src/opengeodeweb_microservice/microservice/data.py index 5dbf691..a5c421f 100644 --- a/src/opengeodeweb_microservice/microservice/data.py +++ b/src/opengeodeweb_microservice/microservice/data.py @@ -1,10 +1,9 @@ from sqlalchemy import String, JSON from sqlalchemy.orm import Mapped, mapped_column -from ..database.session import get_session +from ..database.connection import get_session from .base import Base import uuid - class Data(Base): __tablename__ = "datas" @@ -38,11 +37,14 @@ def create( ) session = get_session() - session.session.add(data_entry) - session.session.flush() + if session: + session.add(data_entry) + session.flush() return data_entry @staticmethod def get(data_id: str) -> "Data | None": session = get_session() - return session.session.get(Data, data_id) + if session: + return session.get(Data, data_id) + return None diff --git a/tests/conftest.py b/tests/conftest.py index 2871ed9..2df1ab4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,10 +1,9 @@ import os import pytest from flask import Flask -from src.opengeodeweb_microservice.database.session import init_database, get_session +from src.opengeodeweb_microservice.database.connection import init_database, get_session from src.opengeodeweb_microservice.microservice.data import Data - @pytest.fixture(scope="session") def app(): app = Flask(__name__) @@ -19,18 +18,14 @@ def app(): _cleanup_database_connections() _remove_test_database(db_path) - def _cleanup_database_connections(): try: session = get_session() - if session and hasattr(session, "session"): - session.session.close() - if session and hasattr(session, "engine"): - session.engine.dispose() + if session: + session.close() except Exception: pass - def _remove_test_database(db_path: str): if os.path.exists(db_path): try: @@ -38,22 +33,20 @@ def _remove_test_database(db_path: str): except PermissionError: pass - @pytest.fixture def app_context(app): with app.app_context(): yield - @pytest.fixture def clean_database(app_context): session = get_session() if session: - session.session.query(Data).delete() - session.session.commit() + session.query(Data).delete() + session.commit() yield if session: try: - session.session.rollback() + session.rollback() except Exception: pass diff --git a/tests/test_connection.py b/tests/test_connection.py index 125069b..7c25f02 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1,35 +1,11 @@ -import pytest -from src.opengeodeweb_microservice.database.session import ( +from src.opengeodeweb_microservice.database.connection import ( get_session, get_database_connection, ) from src.opengeodeweb_microservice.microservice.data import Data - def test_database_connection_basic(app_context): session = get_session() assert session is not None - assert session.session is not None connection = get_database_connection() assert connection is not None - - -def test_data_crud_operations(app_context, clean_database): - data = Data.create(geode_object="test_object", input_file="test.txt") - assert data.id is not None - session = get_session() - session.session.commit() - retrieved = Data.get(data.id) - assert retrieved is not None - assert retrieved.geode_object == "test_object" - non_existent = Data.get("fake_id") - assert non_existent is None - - -def test_data_with_additional_files(app_context, clean_database): - files = ["file1.txt", "file2.txt"] - data = Data.create(geode_object="test_files", additional_files=files) - session = get_session() - session.session.commit() - retrieved = Data.get(data.id) - assert retrieved.additional_files == files diff --git a/tests/test_database.py b/tests/test_database.py index 06ff861..7ffe23c 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -1,28 +1,21 @@ -import pytest -from src.opengeodeweb_microservice.database.session import get_session +from src.opengeodeweb_microservice.database.connection import get_session from src.opengeodeweb_microservice.microservice.data import Data - -def test_database_session_available(app_context): - session = get_session() - assert session is not None - assert session.session is not None - - -def test_data_crud_operations(app_context, clean_database): - data = Data.create( - geode_object="test_object", - input_file="test.txt", - additional_files=["file1.txt", "file2.txt"], - ) +def test_data_crud_operations(): + data = Data.create(geode_object="test_object", input_file="test.txt") assert data.id is not None - assert data.geode_object == "test_object" - assert data.additional_files == ["file1.txt", "file2.txt"] session = get_session() - session.session.commit() + session.commit() retrieved = Data.get(data.id) assert retrieved is not None assert retrieved.geode_object == "test_object" - assert retrieved.additional_files == ["file1.txt", "file2.txt"] non_existent = Data.get("fake_id") assert non_existent is None + +def test_data_with_additional_files(): + files = ["file1.txt", "file2.txt"] + data = Data.create(geode_object="test_files", additional_files=files) + session = get_session() + session.commit() + retrieved = Data.get(data.id) + assert retrieved.additional_files == files From 05d6194fc239f5cb6fd2504b3e97034cfd62bbbd Mon Sep 17 00:00:00 2001 From: MaxNumerique <144453705+MaxNumerique@users.noreply.github.com> Date: Wed, 10 Sep 2025 12:16:47 +0000 Subject: [PATCH 14/43] Apply prepare changes --- src/opengeodeweb_microservice/app_config.py | 2 ++ src/opengeodeweb_microservice/database/connection.py | 4 ++++ src/opengeodeweb_microservice/microservice/data.py | 1 + tests/conftest.py | 5 +++++ tests/test_connection.py | 1 + tests/test_database.py | 2 ++ 6 files changed, 15 insertions(+) diff --git a/src/opengeodeweb_microservice/app_config.py b/src/opengeodeweb_microservice/app_config.py index d33e8e9..0123ccb 100644 --- a/src/opengeodeweb_microservice/app_config.py +++ b/src/opengeodeweb_microservice/app_config.py @@ -1,9 +1,11 @@ import os from .database.connection import DATABASE_FILENAME + class Config: pass + class ProdConfig(Config): DATA_FOLDER_PATH = "/data" SQLALCHEMY_DATABASE_URI = f"sqlite:///{os.path.abspath( diff --git a/src/opengeodeweb_microservice/database/connection.py b/src/opengeodeweb_microservice/database/connection.py index e91773c..ebaea6d 100644 --- a/src/opengeodeweb_microservice/database/connection.py +++ b/src/opengeodeweb_microservice/database/connection.py @@ -9,6 +9,7 @@ db: Optional[SQLAlchemy] = None + def init_database(app: Flask, db_filename: str = DATABASE_FILENAME) -> SQLAlchemy: global db if db is None: @@ -18,13 +19,16 @@ def init_database(app: Flask, db_filename: str = DATABASE_FILENAME) -> SQLAlchem db.create_all() return db + def get_database() -> Optional[SQLAlchemy]: return db + def get_session(): if db is None: return None return db.session + def get_database_connection(): return get_database() diff --git a/src/opengeodeweb_microservice/microservice/data.py b/src/opengeodeweb_microservice/microservice/data.py index a5c421f..772b571 100644 --- a/src/opengeodeweb_microservice/microservice/data.py +++ b/src/opengeodeweb_microservice/microservice/data.py @@ -4,6 +4,7 @@ from .base import Base import uuid + class Data(Base): __tablename__ = "datas" diff --git a/tests/conftest.py b/tests/conftest.py index 2df1ab4..db783d1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,6 +4,7 @@ from src.opengeodeweb_microservice.database.connection import init_database, get_session from src.opengeodeweb_microservice.microservice.data import Data + @pytest.fixture(scope="session") def app(): app = Flask(__name__) @@ -18,6 +19,7 @@ def app(): _cleanup_database_connections() _remove_test_database(db_path) + def _cleanup_database_connections(): try: session = get_session() @@ -26,6 +28,7 @@ def _cleanup_database_connections(): except Exception: pass + def _remove_test_database(db_path: str): if os.path.exists(db_path): try: @@ -33,11 +36,13 @@ def _remove_test_database(db_path: str): except PermissionError: pass + @pytest.fixture def app_context(app): with app.app_context(): yield + @pytest.fixture def clean_database(app_context): session = get_session() diff --git a/tests/test_connection.py b/tests/test_connection.py index 7c25f02..ccd1ad7 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -4,6 +4,7 @@ ) from src.opengeodeweb_microservice.microservice.data import Data + def test_database_connection_basic(app_context): session = get_session() assert session is not None diff --git a/tests/test_database.py b/tests/test_database.py index 7ffe23c..c8d82e4 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -1,6 +1,7 @@ from src.opengeodeweb_microservice.database.connection import get_session from src.opengeodeweb_microservice.microservice.data import Data + def test_data_crud_operations(): data = Data.create(geode_object="test_object", input_file="test.txt") assert data.id is not None @@ -12,6 +13,7 @@ def test_data_crud_operations(): non_existent = Data.get("fake_id") assert non_existent is None + def test_data_with_additional_files(): files = ["file1.txt", "file2.txt"] data = Data.create(geode_object="test_files", additional_files=files) From fefecf1c9f90fa4bbd676919daa62b8a5bc835c9 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 10 Sep 2025 14:26:10 +0200 Subject: [PATCH 15/43] refacto --- .../database/connection.py | 8 ++------ tests/conftest.py | 18 ++++++++---------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/opengeodeweb_microservice/database/connection.py b/src/opengeodeweb_microservice/database/connection.py index e91773c..3b9b1f1 100644 --- a/src/opengeodeweb_microservice/database/connection.py +++ b/src/opengeodeweb_microservice/database/connection.py @@ -6,7 +6,6 @@ from ..microservice.base import Base DATABASE_FILENAME = "project.db" - db: Optional[SQLAlchemy] = None def init_database(app: Flask, db_filename: str = DATABASE_FILENAME) -> SQLAlchemy: @@ -22,9 +21,6 @@ def get_database() -> Optional[SQLAlchemy]: return db def get_session(): - if db is None: - return None - return db.session + return db.session if db else None -def get_database_connection(): - return get_database() +get_database_connection = get_database diff --git a/tests/conftest.py b/tests/conftest.py index 2df1ab4..1ed4c0f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,26 +7,24 @@ @pytest.fixture(scope="session") def app(): app = Flask(__name__) - app.config["TESTING"] = True - BASE_DIR = os.path.abspath(os.path.dirname(__file__)) - db_path = os.path.join(BASE_DIR, "test_project.db") - app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{db_path}" - app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False + app.config.update({ + "TESTING": True, + "SQLALCHEMY_DATABASE_URI": f"sqlite:///{os.path.join(os.path.dirname(__file__), 'test_project.db')}", + "SQLALCHEMY_TRACK_MODIFICATIONS": False + }) with app.app_context(): init_database(app, "test_project.db") yield app - _cleanup_database_connections() - _remove_test_database(db_path) + _cleanup_database() -def _cleanup_database_connections(): +def _cleanup_database(): try: session = get_session() if session: session.close() except Exception: pass - -def _remove_test_database(db_path: str): + db_path = os.path.join(os.path.dirname(__file__), "test_project.db") if os.path.exists(db_path): try: os.remove(db_path) From ae244be8f06b80ad243dec20c7f2f9c4802372eb Mon Sep 17 00:00:00 2001 From: MaxNumerique <144453705+MaxNumerique@users.noreply.github.com> Date: Wed, 10 Sep 2025 12:27:07 +0000 Subject: [PATCH 16/43] Apply prepare changes --- .../database/connection.py | 1 + tests/conftest.py | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/opengeodeweb_microservice/database/connection.py b/src/opengeodeweb_microservice/database/connection.py index 465af41..00fa0a7 100644 --- a/src/opengeodeweb_microservice/database/connection.py +++ b/src/opengeodeweb_microservice/database/connection.py @@ -26,4 +26,5 @@ def get_database() -> Optional[SQLAlchemy]: def get_session(): return db.session if db else None + get_database_connection = get_database diff --git a/tests/conftest.py b/tests/conftest.py index 9b5b7cf..3b07ffd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,16 +8,19 @@ @pytest.fixture(scope="session") def app(): app = Flask(__name__) - app.config.update({ - "TESTING": True, - "SQLALCHEMY_DATABASE_URI": f"sqlite:///{os.path.join(os.path.dirname(__file__), 'test_project.db')}", - "SQLALCHEMY_TRACK_MODIFICATIONS": False - }) + app.config.update( + { + "TESTING": True, + "SQLALCHEMY_DATABASE_URI": f"sqlite:///{os.path.join(os.path.dirname(__file__), 'test_project.db')}", + "SQLALCHEMY_TRACK_MODIFICATIONS": False, + } + ) with app.app_context(): init_database(app, "test_project.db") yield app _cleanup_database() + def _cleanup_database(): try: session = get_session() From 0b157bd75c70bc9413ae431575774a625940e60e Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 10 Sep 2025 14:35:49 +0200 Subject: [PATCH 17/43] mypy --- src/opengeodeweb_microservice/database/connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/opengeodeweb_microservice/database/connection.py b/src/opengeodeweb_microservice/database/connection.py index 465af41..3617b09 100644 --- a/src/opengeodeweb_microservice/database/connection.py +++ b/src/opengeodeweb_microservice/database/connection.py @@ -23,7 +23,7 @@ def get_database() -> Optional[SQLAlchemy]: return db -def get_session(): +def get_session() -> Optional[Session]: return db.session if db else None get_database_connection = get_database From 67f0661b23b148f1dd0604febdff0623ef11b3e2 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 10 Sep 2025 14:37:01 +0200 Subject: [PATCH 18/43] Session import missing --- src/opengeodeweb_microservice/database/connection.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/opengeodeweb_microservice/database/connection.py b/src/opengeodeweb_microservice/database/connection.py index e5d4553..8bd43f4 100644 --- a/src/opengeodeweb_microservice/database/connection.py +++ b/src/opengeodeweb_microservice/database/connection.py @@ -1,6 +1,7 @@ """Database connection management""" from typing import Optional +from sqlalchemy.orm import Session from flask import Flask from flask_sqlalchemy import SQLAlchemy from ..microservice.base import Base From 6caeddb990d69ce743192680adfdb02d0a7428e8 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 10 Sep 2025 14:40:15 +0200 Subject: [PATCH 19/43] scope_session --- src/opengeodeweb_microservice/database/connection.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/opengeodeweb_microservice/database/connection.py b/src/opengeodeweb_microservice/database/connection.py index 8bd43f4..d382a19 100644 --- a/src/opengeodeweb_microservice/database/connection.py +++ b/src/opengeodeweb_microservice/database/connection.py @@ -1,7 +1,7 @@ """Database connection management""" from typing import Optional -from sqlalchemy.orm import Session +from sqlalchemy.orm import Session, scoped_session from flask import Flask from flask_sqlalchemy import SQLAlchemy from ..microservice.base import Base @@ -24,7 +24,7 @@ def get_database() -> Optional[SQLAlchemy]: return db -def get_session() -> Optional[Session]: +def get_session() -> Optional[scoped_session[Session]]: return db.session if db else None From 0c596a50cb31f0b49d0b1d882b100e6f7de29cd5 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 10 Sep 2025 14:55:22 +0200 Subject: [PATCH 20/43] checkers --- src/opengeodeweb_microservice/database/connection.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/opengeodeweb_microservice/database/connection.py b/src/opengeodeweb_microservice/database/connection.py index d382a19..6a6be3a 100644 --- a/src/opengeodeweb_microservice/database/connection.py +++ b/src/opengeodeweb_microservice/database/connection.py @@ -1,7 +1,7 @@ """Database connection management""" -from typing import Optional -from sqlalchemy.orm import Session, scoped_session +from typing import Optional, Any +from sqlalchemy.orm import Session from flask import Flask from flask_sqlalchemy import SQLAlchemy from ..microservice.base import Base @@ -24,7 +24,7 @@ def get_database() -> Optional[SQLAlchemy]: return db -def get_session() -> Optional[scoped_session[Session]]: +def get_session() -> Optional[Any]: return db.session if db else None From 58488fc48a7822fb639b7070e949c23942ed5f95 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 10 Sep 2025 14:58:14 +0200 Subject: [PATCH 21/43] temporary npm: faalse --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 22c4bdb..1fe7fd3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,5 +10,5 @@ jobs: test: uses: Geode-solutions/actions/.github/workflows/py-test.yml@master with: - npm: true + npm: false secrets: inherit From b647e1ef328c1716d372c8e3433d4727cb75e3d2 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 10 Sep 2025 15:18:21 +0200 Subject: [PATCH 22/43] get_session type --- .github/workflows/test_pr.yml | 2 +- src/opengeodeweb_microservice/database/connection.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_pr.yml b/.github/workflows/test_pr.yml index 43d7b73..cd54d43 100644 --- a/.github/workflows/test_pr.yml +++ b/.github/workflows/test_pr.yml @@ -8,5 +8,5 @@ jobs: test: uses: Geode-solutions/actions/.github/workflows/py-test-pr.yml@master with: - npm: true + npm: false secrets: inherit diff --git a/src/opengeodeweb_microservice/database/connection.py b/src/opengeodeweb_microservice/database/connection.py index 6a6be3a..17493f0 100644 --- a/src/opengeodeweb_microservice/database/connection.py +++ b/src/opengeodeweb_microservice/database/connection.py @@ -24,7 +24,7 @@ def get_database() -> Optional[SQLAlchemy]: return db -def get_session() -> Optional[Any]: +def get_session() -> Optional[Session]: return db.session if db else None From eccc97675d702407e70c1ef8a6ff4cdf1159fb1e Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 10 Sep 2025 15:21:02 +0200 Subject: [PATCH 23/43] checkers please --- src/opengeodeweb_microservice/database/connection.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/opengeodeweb_microservice/database/connection.py b/src/opengeodeweb_microservice/database/connection.py index 17493f0..421c47e 100644 --- a/src/opengeodeweb_microservice/database/connection.py +++ b/src/opengeodeweb_microservice/database/connection.py @@ -1,9 +1,10 @@ """Database connection management""" -from typing import Optional, Any -from sqlalchemy.orm import Session +from typing import Optional +from sqlalchemy.orm import scoped_session from flask import Flask from flask_sqlalchemy import SQLAlchemy +from flask_sqlalchemy.session import Session from ..microservice.base import Base DATABASE_FILENAME = "project.db" @@ -24,7 +25,7 @@ def get_database() -> Optional[SQLAlchemy]: return db -def get_session() -> Optional[Session]: +def get_session() -> Optional[scoped_session[Session]]: return db.session if db else None From 683fd6b48f65ba00762d488ebb3c62bb0be67999 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 10 Sep 2025 15:35:43 +0200 Subject: [PATCH 24/43] app_config not needed ? --- src/opengeodeweb_microservice/app_config.py | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 src/opengeodeweb_microservice/app_config.py diff --git a/src/opengeodeweb_microservice/app_config.py b/src/opengeodeweb_microservice/app_config.py deleted file mode 100644 index 0123ccb..0000000 --- a/src/opengeodeweb_microservice/app_config.py +++ /dev/null @@ -1,21 +0,0 @@ -import os -from .database.connection import DATABASE_FILENAME - - -class Config: - pass - - -class ProdConfig(Config): - DATA_FOLDER_PATH = "/data" - SQLALCHEMY_DATABASE_URI = f"sqlite:///{os.path.abspath( - os.path.join(DATA_FOLDER_PATH, DATABASE_FILENAME) - )}" - - -class DevConfig(Config): - BASE_DIR = os.path.dirname(os.path.abspath(__file__)) - DATA_FOLDER_PATH = os.path.join(BASE_DIR, "data") - SQLALCHEMY_DATABASE_URI = f"sqlite:///{os.path.join( - BASE_DIR, DATA_FOLDER_PATH, DATABASE_FILENAME - )}" From 3e46a2e21f6d3d0ccb0fbeb0ba5b267635709497 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 10 Sep 2025 15:47:15 +0200 Subject: [PATCH 25/43] changed position of get_dataabase_connection to fit OGW-Back initial position --- src/opengeodeweb_microservice/database/connection.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/opengeodeweb_microservice/database/connection.py b/src/opengeodeweb_microservice/database/connection.py index 421c47e..ad6551c 100644 --- a/src/opengeodeweb_microservice/database/connection.py +++ b/src/opengeodeweb_microservice/database/connection.py @@ -20,6 +20,8 @@ def init_database(app: Flask, db_filename: str = DATABASE_FILENAME) -> SQLAlchem db.create_all() return db +get_database_connection = get_database + def get_database() -> Optional[SQLAlchemy]: return db @@ -27,6 +29,3 @@ def get_database() -> Optional[SQLAlchemy]: def get_session() -> Optional[scoped_session[Session]]: return db.session if db else None - - -get_database_connection = get_database From 2afed63b697b7f16cd43404aa4ae1b66db5bcf03 Mon Sep 17 00:00:00 2001 From: MaxNumerique <144453705+MaxNumerique@users.noreply.github.com> Date: Wed, 10 Sep 2025 13:47:36 +0000 Subject: [PATCH 26/43] Apply prepare changes --- src/opengeodeweb_microservice/database/connection.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/opengeodeweb_microservice/database/connection.py b/src/opengeodeweb_microservice/database/connection.py index ad6551c..5632cc6 100644 --- a/src/opengeodeweb_microservice/database/connection.py +++ b/src/opengeodeweb_microservice/database/connection.py @@ -20,6 +20,7 @@ def init_database(app: Flask, db_filename: str = DATABASE_FILENAME) -> SQLAlchem db.create_all() return db + get_database_connection = get_database From 76e4527ae292aca72fa261567a43752d472a7d51 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Wed, 10 Sep 2025 16:33:23 +0200 Subject: [PATCH 27/43] moved get_database_connection after definition of get_database --- src/opengeodeweb_microservice/database/connection.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/opengeodeweb_microservice/database/connection.py b/src/opengeodeweb_microservice/database/connection.py index ad6551c..b582fb3 100644 --- a/src/opengeodeweb_microservice/database/connection.py +++ b/src/opengeodeweb_microservice/database/connection.py @@ -20,12 +20,11 @@ def init_database(app: Flask, db_filename: str = DATABASE_FILENAME) -> SQLAlchem db.create_all() return db -get_database_connection = get_database - def get_database() -> Optional[SQLAlchemy]: return db +get_database_connection = get_database def get_session() -> Optional[scoped_session[Session]]: return db.session if db else None From 5b1f7bc4c7c61974e32a0aa84e13f69d0461f95d Mon Sep 17 00:00:00 2001 From: MaxNumerique <144453705+MaxNumerique@users.noreply.github.com> Date: Wed, 10 Sep 2025 14:33:56 +0000 Subject: [PATCH 28/43] Apply prepare changes --- src/opengeodeweb_microservice/database/connection.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/opengeodeweb_microservice/database/connection.py b/src/opengeodeweb_microservice/database/connection.py index b582fb3..87b4e7f 100644 --- a/src/opengeodeweb_microservice/database/connection.py +++ b/src/opengeodeweb_microservice/database/connection.py @@ -24,7 +24,9 @@ def init_database(app: Flask, db_filename: str = DATABASE_FILENAME) -> SQLAlchem def get_database() -> Optional[SQLAlchemy]: return db + get_database_connection = get_database + def get_session() -> Optional[scoped_session[Session]]: return db.session if db else None From 61b9df00fa210b1b652bad756fa9f35834da5f3a Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Thu, 11 Sep 2025 12:11:40 +0200 Subject: [PATCH 29/43] session.commit() --- src/opengeodeweb_microservice/microservice/data.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/opengeodeweb_microservice/microservice/data.py b/src/opengeodeweb_microservice/microservice/data.py index 772b571..b3eba54 100644 --- a/src/opengeodeweb_microservice/microservice/data.py +++ b/src/opengeodeweb_microservice/microservice/data.py @@ -41,6 +41,7 @@ def create( if session: session.add(data_entry) session.flush() + session.commit() return data_entry @staticmethod From 2d9b9eb0097d50c4b124042aa99e05287702236e Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Thu, 11 Sep 2025 14:38:47 +0200 Subject: [PATCH 30/43] database --- .../lib/opengeodeweb_microservice/__init__.py | 0 .../database/__init__.py | 0 .../database/connection.py | 32 ++++++++++++ .../microservice/__init__.py | 0 .../microservice/base.py | 7 +++ .../microservice/data.py | 52 +++++++++++++++++++ tests/conftest.py | 4 +- 7 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 build/lib/opengeodeweb_microservice/__init__.py create mode 100644 build/lib/opengeodeweb_microservice/database/__init__.py create mode 100644 build/lib/opengeodeweb_microservice/database/connection.py create mode 100644 build/lib/opengeodeweb_microservice/microservice/__init__.py create mode 100644 build/lib/opengeodeweb_microservice/microservice/base.py create mode 100644 build/lib/opengeodeweb_microservice/microservice/data.py diff --git a/build/lib/opengeodeweb_microservice/__init__.py b/build/lib/opengeodeweb_microservice/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/opengeodeweb_microservice/database/__init__.py b/build/lib/opengeodeweb_microservice/database/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/opengeodeweb_microservice/database/connection.py b/build/lib/opengeodeweb_microservice/database/connection.py new file mode 100644 index 0000000..87b4e7f --- /dev/null +++ b/build/lib/opengeodeweb_microservice/database/connection.py @@ -0,0 +1,32 @@ +"""Database connection management""" + +from typing import Optional +from sqlalchemy.orm import scoped_session +from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from flask_sqlalchemy.session import Session +from ..microservice.base import Base + +DATABASE_FILENAME = "project.db" +db: Optional[SQLAlchemy] = None + + +def init_database(app: Flask, db_filename: str = DATABASE_FILENAME) -> SQLAlchemy: + global db + if db is None: + db = SQLAlchemy(model_class=Base) + db.init_app(app) + with app.app_context(): + db.create_all() + return db + + +def get_database() -> Optional[SQLAlchemy]: + return db + + +get_database_connection = get_database + + +def get_session() -> Optional[scoped_session[Session]]: + return db.session if db else None diff --git a/build/lib/opengeodeweb_microservice/microservice/__init__.py b/build/lib/opengeodeweb_microservice/microservice/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/opengeodeweb_microservice/microservice/base.py b/build/lib/opengeodeweb_microservice/microservice/base.py new file mode 100644 index 0000000..8bb12e6 --- /dev/null +++ b/build/lib/opengeodeweb_microservice/microservice/base.py @@ -0,0 +1,7 @@ +from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from sqlalchemy.orm import DeclarativeBase + + +class Base(DeclarativeBase): + pass diff --git a/build/lib/opengeodeweb_microservice/microservice/data.py b/build/lib/opengeodeweb_microservice/microservice/data.py new file mode 100644 index 0000000..b3eba54 --- /dev/null +++ b/build/lib/opengeodeweb_microservice/microservice/data.py @@ -0,0 +1,52 @@ +from sqlalchemy import String, JSON +from sqlalchemy.orm import Mapped, mapped_column +from ..database.connection import get_session +from .base import Base +import uuid + + +class Data(Base): + __tablename__ = "datas" + + id: Mapped[str] = mapped_column( + String, primary_key=True, default=lambda: str(uuid.uuid4()).replace("-", "") + ) + native_file_name: Mapped[str] = mapped_column(String, nullable=False) + viewable_file_name: Mapped[str] = mapped_column(String, nullable=False) + geode_object: Mapped[str] = mapped_column(String, nullable=False) + + light_viewable: Mapped[str | None] = mapped_column(String, nullable=True) + input_file: Mapped[str | None] = mapped_column(String, nullable=True) + additional_files: Mapped[list[str] | None] = mapped_column(JSON, nullable=True) + + @staticmethod + def create( + geode_object: str, + input_file: str | None = None, + additional_files: list[str] | None = None, + ) -> "Data": + input_file = input_file if input_file is not None else "" + additional_files = additional_files if additional_files is not None else [] + + data_entry = Data( + geode_object=geode_object, + input_file=input_file, + additional_files=additional_files, + native_file_name="", + viewable_file_name="", + light_viewable=None, + ) + + session = get_session() + if session: + session.add(data_entry) + session.flush() + session.commit() + return data_entry + + @staticmethod + def get(data_id: str) -> "Data | None": + session = get_session() + if session: + return session.get(Data, data_id) + return None diff --git a/tests/conftest.py b/tests/conftest.py index 3b07ffd..e1be848 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,8 @@ import os import pytest from flask import Flask -from src.opengeodeweb_microservice.database.connection import init_database, get_session -from src.opengeodeweb_microservice.microservice.data import Data +from opengeodeweb_microservice.database.connection import init_database, get_session +from opengeodeweb_microservice.microservice.data import Data @pytest.fixture(scope="session") From 0838a10eca2aa9b99be97096a8b6767045b292ab Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Thu, 11 Sep 2025 14:41:20 +0200 Subject: [PATCH 31/43] delete build folder and add it to gitignore --- .gitignore | 3 +- .../lib/opengeodeweb_microservice/__init__.py | 0 .../database/__init__.py | 0 .../database/connection.py | 32 ------------ .../microservice/__init__.py | 0 .../microservice/base.py | 7 --- .../microservice/data.py | 52 ------------------- 7 files changed, 2 insertions(+), 92 deletions(-) delete mode 100644 build/lib/opengeodeweb_microservice/__init__.py delete mode 100644 build/lib/opengeodeweb_microservice/database/__init__.py delete mode 100644 build/lib/opengeodeweb_microservice/database/connection.py delete mode 100644 build/lib/opengeodeweb_microservice/microservice/__init__.py delete mode 100644 build/lib/opengeodeweb_microservice/microservice/base.py delete mode 100644 build/lib/opengeodeweb_microservice/microservice/data.py diff --git a/.gitignore b/.gitignore index 11aedd1..5e7e0c8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ venv __pycache__ node_modules .mypy_cache -*.db \ No newline at end of file +*.db +/build \ No newline at end of file diff --git a/build/lib/opengeodeweb_microservice/__init__.py b/build/lib/opengeodeweb_microservice/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/build/lib/opengeodeweb_microservice/database/__init__.py b/build/lib/opengeodeweb_microservice/database/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/build/lib/opengeodeweb_microservice/database/connection.py b/build/lib/opengeodeweb_microservice/database/connection.py deleted file mode 100644 index 87b4e7f..0000000 --- a/build/lib/opengeodeweb_microservice/database/connection.py +++ /dev/null @@ -1,32 +0,0 @@ -"""Database connection management""" - -from typing import Optional -from sqlalchemy.orm import scoped_session -from flask import Flask -from flask_sqlalchemy import SQLAlchemy -from flask_sqlalchemy.session import Session -from ..microservice.base import Base - -DATABASE_FILENAME = "project.db" -db: Optional[SQLAlchemy] = None - - -def init_database(app: Flask, db_filename: str = DATABASE_FILENAME) -> SQLAlchemy: - global db - if db is None: - db = SQLAlchemy(model_class=Base) - db.init_app(app) - with app.app_context(): - db.create_all() - return db - - -def get_database() -> Optional[SQLAlchemy]: - return db - - -get_database_connection = get_database - - -def get_session() -> Optional[scoped_session[Session]]: - return db.session if db else None diff --git a/build/lib/opengeodeweb_microservice/microservice/__init__.py b/build/lib/opengeodeweb_microservice/microservice/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/build/lib/opengeodeweb_microservice/microservice/base.py b/build/lib/opengeodeweb_microservice/microservice/base.py deleted file mode 100644 index 8bb12e6..0000000 --- a/build/lib/opengeodeweb_microservice/microservice/base.py +++ /dev/null @@ -1,7 +0,0 @@ -from flask import Flask -from flask_sqlalchemy import SQLAlchemy -from sqlalchemy.orm import DeclarativeBase - - -class Base(DeclarativeBase): - pass diff --git a/build/lib/opengeodeweb_microservice/microservice/data.py b/build/lib/opengeodeweb_microservice/microservice/data.py deleted file mode 100644 index b3eba54..0000000 --- a/build/lib/opengeodeweb_microservice/microservice/data.py +++ /dev/null @@ -1,52 +0,0 @@ -from sqlalchemy import String, JSON -from sqlalchemy.orm import Mapped, mapped_column -from ..database.connection import get_session -from .base import Base -import uuid - - -class Data(Base): - __tablename__ = "datas" - - id: Mapped[str] = mapped_column( - String, primary_key=True, default=lambda: str(uuid.uuid4()).replace("-", "") - ) - native_file_name: Mapped[str] = mapped_column(String, nullable=False) - viewable_file_name: Mapped[str] = mapped_column(String, nullable=False) - geode_object: Mapped[str] = mapped_column(String, nullable=False) - - light_viewable: Mapped[str | None] = mapped_column(String, nullable=True) - input_file: Mapped[str | None] = mapped_column(String, nullable=True) - additional_files: Mapped[list[str] | None] = mapped_column(JSON, nullable=True) - - @staticmethod - def create( - geode_object: str, - input_file: str | None = None, - additional_files: list[str] | None = None, - ) -> "Data": - input_file = input_file if input_file is not None else "" - additional_files = additional_files if additional_files is not None else [] - - data_entry = Data( - geode_object=geode_object, - input_file=input_file, - additional_files=additional_files, - native_file_name="", - viewable_file_name="", - light_viewable=None, - ) - - session = get_session() - if session: - session.add(data_entry) - session.flush() - session.commit() - return data_entry - - @staticmethod - def get(data_id: str) -> "Data | None": - session = get_session() - if session: - return session.get(Data, data_id) - return None From 98f4736680dafceaa0b004349e684b453771fc1e Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Thu, 11 Sep 2025 15:17:42 +0200 Subject: [PATCH 32/43] removed session.commit() --- src/opengeodeweb_microservice/microservice/data.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/opengeodeweb_microservice/microservice/data.py b/src/opengeodeweb_microservice/microservice/data.py index b3eba54..772b571 100644 --- a/src/opengeodeweb_microservice/microservice/data.py +++ b/src/opengeodeweb_microservice/microservice/data.py @@ -41,7 +41,6 @@ def create( if session: session.add(data_entry) session.flush() - session.commit() return data_entry @staticmethod From b0a915465b218ebc127ac8a1ac1b2ddfc191946b Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Thu, 11 Sep 2025 15:21:43 +0200 Subject: [PATCH 33/43] imports --- src/opengeodeweb_microservice/microservice/data.py | 1 + tests/conftest.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/opengeodeweb_microservice/microservice/data.py b/src/opengeodeweb_microservice/microservice/data.py index 772b571..b3eba54 100644 --- a/src/opengeodeweb_microservice/microservice/data.py +++ b/src/opengeodeweb_microservice/microservice/data.py @@ -41,6 +41,7 @@ def create( if session: session.add(data_entry) session.flush() + session.commit() return data_entry @staticmethod diff --git a/tests/conftest.py b/tests/conftest.py index e1be848..3b07ffd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,8 @@ import os import pytest from flask import Flask -from opengeodeweb_microservice.database.connection import init_database, get_session -from opengeodeweb_microservice.microservice.data import Data +from src.opengeodeweb_microservice.database.connection import init_database, get_session +from src.opengeodeweb_microservice.microservice.data import Data @pytest.fixture(scope="session") From 72ea391024dd2901a3589016d0a73d8a017d6fe7 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Thu, 11 Sep 2025 15:44:28 +0200 Subject: [PATCH 34/43] py.typed --- src/opengeodeweb_microservice/py.typed | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/opengeodeweb_microservice/py.typed diff --git a/src/opengeodeweb_microservice/py.typed b/src/opengeodeweb_microservice/py.typed new file mode 100644 index 0000000..e69de29 From d26b62e0e08e63e5b9778a0a8c3dbe23c9b3d6c7 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Thu, 11 Sep 2025 15:48:19 +0200 Subject: [PATCH 35/43] [tool.setuptools.package-data] opengeodeweb_microservice = ["py.typed"] --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 5ca2442..2fd4da1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,3 +25,6 @@ dependencies = { file = ["requirements.txt"] } [tool.setuptools.packages.find] where = ["src"] + +[tool.setuptools.package-data] +opengeodeweb_microservice = ["py.typed"] From 89e62ad35aecd3dc26fecc56052ca9755373ec5e Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Thu, 11 Sep 2025 16:03:40 +0200 Subject: [PATCH 36/43] rm app_context --- tests/test_connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index ccd1ad7..26e578c 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -5,7 +5,7 @@ from src.opengeodeweb_microservice.microservice.data import Data -def test_database_connection_basic(app_context): +def test_database_connection_basic(): session = get_session() assert session is not None connection = get_database_connection() From f4d664a890f7d5fbee0eca25c2fa985b07745664 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Thu, 11 Sep 2025 16:12:44 +0200 Subject: [PATCH 37/43] revert --- tests/test_connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index 26e578c..ccd1ad7 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -5,7 +5,7 @@ from src.opengeodeweb_microservice.microservice.data import Data -def test_database_connection_basic(): +def test_database_connection_basic(app_context): session = get_session() assert session is not None connection = get_database_connection() From f86cfe3b99662b02045a093b77aefd81f865a200 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Thu, 11 Sep 2025 16:17:50 +0200 Subject: [PATCH 38/43] removed connection alias --- src/opengeodeweb_microservice/database/connection.py | 3 --- tests/test_connection.py | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/opengeodeweb_microservice/database/connection.py b/src/opengeodeweb_microservice/database/connection.py index 87b4e7f..fb53a73 100644 --- a/src/opengeodeweb_microservice/database/connection.py +++ b/src/opengeodeweb_microservice/database/connection.py @@ -25,8 +25,5 @@ def get_database() -> Optional[SQLAlchemy]: return db -get_database_connection = get_database - - def get_session() -> Optional[scoped_session[Session]]: return db.session if db else None diff --git a/tests/test_connection.py b/tests/test_connection.py index ccd1ad7..c81de27 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1,6 +1,6 @@ from src.opengeodeweb_microservice.database.connection import ( get_session, - get_database_connection, + get_database, ) from src.opengeodeweb_microservice.microservice.data import Data @@ -8,5 +8,5 @@ def test_database_connection_basic(app_context): session = get_session() assert session is not None - connection = get_database_connection() + connection = get_database() assert connection is not None From e10e321899a841140b37da59d39841f1d120549a Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Thu, 11 Sep 2025 16:41:43 +0200 Subject: [PATCH 39/43] moved files into database folder --- .../{microservice => database}/base.py | 0 src/opengeodeweb_microservice/database/connection.py | 2 +- .../{microservice => database}/data.py | 2 +- src/opengeodeweb_microservice/microservice/__init__.py | 0 tests/conftest.py | 2 +- tests/test_connection.py | 7 ++----- tests/test_database.py | 2 +- 7 files changed, 6 insertions(+), 9 deletions(-) rename src/opengeodeweb_microservice/{microservice => database}/base.py (100%) rename src/opengeodeweb_microservice/{microservice => database}/data.py (97%) delete mode 100644 src/opengeodeweb_microservice/microservice/__init__.py diff --git a/src/opengeodeweb_microservice/microservice/base.py b/src/opengeodeweb_microservice/database/base.py similarity index 100% rename from src/opengeodeweb_microservice/microservice/base.py rename to src/opengeodeweb_microservice/database/base.py diff --git a/src/opengeodeweb_microservice/database/connection.py b/src/opengeodeweb_microservice/database/connection.py index fb53a73..0d76762 100644 --- a/src/opengeodeweb_microservice/database/connection.py +++ b/src/opengeodeweb_microservice/database/connection.py @@ -5,7 +5,7 @@ from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy.session import Session -from ..microservice.base import Base +from .base import Base DATABASE_FILENAME = "project.db" db: Optional[SQLAlchemy] = None diff --git a/src/opengeodeweb_microservice/microservice/data.py b/src/opengeodeweb_microservice/database/data.py similarity index 97% rename from src/opengeodeweb_microservice/microservice/data.py rename to src/opengeodeweb_microservice/database/data.py index b3eba54..97555f7 100644 --- a/src/opengeodeweb_microservice/microservice/data.py +++ b/src/opengeodeweb_microservice/database/data.py @@ -1,6 +1,6 @@ from sqlalchemy import String, JSON from sqlalchemy.orm import Mapped, mapped_column -from ..database.connection import get_session +from .connection import get_session from .base import Base import uuid diff --git a/src/opengeodeweb_microservice/microservice/__init__.py b/src/opengeodeweb_microservice/microservice/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/conftest.py b/tests/conftest.py index 3b07ffd..3852862 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,7 +2,7 @@ import pytest from flask import Flask from src.opengeodeweb_microservice.database.connection import init_database, get_session -from src.opengeodeweb_microservice.microservice.data import Data +from src.opengeodeweb_microservice.database.data import Data @pytest.fixture(scope="session") diff --git a/tests/test_connection.py b/tests/test_connection.py index c81de27..fe3748e 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1,8 +1,5 @@ -from src.opengeodeweb_microservice.database.connection import ( - get_session, - get_database, -) -from src.opengeodeweb_microservice.microservice.data import Data +from src.opengeodeweb_microservice.database.connection import get_session, get_database +from src.opengeodeweb_microservice.database.data import Data def test_database_connection_basic(app_context): diff --git a/tests/test_database.py b/tests/test_database.py index c8d82e4..8c7974b 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -1,5 +1,5 @@ from src.opengeodeweb_microservice.database.connection import get_session -from src.opengeodeweb_microservice.microservice.data import Data +from src.opengeodeweb_microservice.database.data import Data def test_data_crud_operations(): From 640bc7ec87b1eee5c755bdb69e5284ce8bc6032c Mon Sep 17 00:00:00 2001 From: Maximilien <144453705+MaxNumerique@users.noreply.github.com> Date: Thu, 11 Sep 2025 17:03:16 +0200 Subject: [PATCH 40/43] Update .github/workflows/deploy.yml Co-authored-by: Arnaud Botella --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 35ffc1c..df0e06a 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -7,6 +7,6 @@ jobs: deploy: uses: Geode-solutions/actions/.github/workflows/py-deploy.yml@master with: - npm: true + npm: false secrets: inherit \ No newline at end of file From 6b53bb91cea7004442469175decadf97d033ce65 Mon Sep 17 00:00:00 2001 From: Maximilien <144453705+MaxNumerique@users.noreply.github.com> Date: Fri, 12 Sep 2025 09:08:41 +0200 Subject: [PATCH 41/43] Update pyproject.toml Co-authored-by: Arnaud Botella --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2fd4da1..2059899 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "OpenGeodeWeb-Microservice" -version = "1.0.0" +version = "0.0.0" dynamic = ["dependencies"] authors = [{ name = "Geode-solutions", email = "team-web@geode-solutions.com" }] description = "Database model and ORM layer for OpenGeodeWeb ecosystem" From 101c8bfaa1cfa4066b12e02207c89e5e61b0b935 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Fri, 12 Sep 2025 09:19:43 +0200 Subject: [PATCH 42/43] freeze requirements --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 7a9f75b..b3c58cb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -SQLAlchemy>=2.0.0 -Flask-SQLAlchemy>=3.1.1 \ No newline at end of file +SQLAlchemy==2.0.0 +Flask-SQLAlchemy==3.1.1 \ No newline at end of file From f09c60375cc8335fc999128cdd08348686d36f64 Mon Sep 17 00:00:00 2001 From: MaxNumerique Date: Fri, 12 Sep 2025 09:23:18 +0200 Subject: [PATCH 43/43] fix requirements --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b3c58cb..939b65c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -SQLAlchemy==2.0.0 +SQLAlchemy==2.0.43 Flask-SQLAlchemy==3.1.1 \ No newline at end of file