diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..88328ed --- /dev/null +++ b/.dockerignore @@ -0,0 +1,978 @@ +# flyctl launch added from .gitignore +# Created by https://www.toptal.com/developers/gitignore/api/vim,node,data,emacs,python,pycharm,executable,sublimetext,visualstudio,visualstudiocode +# Edit at https://www.toptal.com/developers/gitignore?templates=vim,node,data,emacs,python,pycharm,executable,sublimetext,visualstudio,visualstudiocode + +### Data ### +**/*.csv +**/*.dat +**/*.efx +**/*.gbr +**/*.key +**/*.pps +**/*.ppt +**/*.pptx +**/*.sdf +**/*.tax2010 +**/*.vcf +**/*.xml + +### Emacs ### +# -*- mode: gitignore; -*- +**/*~ +**/\#*\# +.emacs.desktop +.emacs.desktop.lock +**/*.elc +**/auto-save-list +**/tramp +**/.\#* + +# Org-mode +**/.org-id-locations +**/*_archive + +# flymake-mode +**/*_flymake.* + +# eshell files +eshell/history +eshell/lastdir + +# elpa packages +elpa + +# reftex files +**/*.rel + +# AUCTeX auto folder +auto + +# cask packages +**/.cask +**/dist + +# Flycheck +**/flycheck_*.el + +# server auth directory +server + +# projectiles files +**/.projectile + +# directory configuration +**/.dir-locals.el + +# network security +network-security.data + + +### Executable ### +**/*.app +**/*.bat +**/*.cgi +**/*.com +**/*.exe +**/*.gadget +**/*.jar +**/*.pif +**/*.vb +**/*.wsf + +### Node ### +# Logs +**/logs +**/*.log +**/npm-debug.log* +**/yarn-debug.log* +**/yarn-error.log* +**/lerna-debug.log* +**/.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +**/report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +**/pids +**/*.pid +**/*.seed +**/*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +**/lib-cov + +# Coverage directory used by tools like istanbul +**/coverage +**/*.lcov + +# nyc test coverage +**/.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +**/.grunt + +# Bower dependency directory (https://bower.io/) +**/bower_components + +# node-waf configuration +**/.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +**/build/Release + +# Dependency directories +**/node_modules +**/jspm_packages + +# Snowpack dependency directory (https://snowpack.dev/) +**/web_modules + +# TypeScript cache +**/*.tsbuildinfo + +# Optional npm cache directory +**/.npm + +# Optional eslint cache +**/.eslintcache + +# Optional stylelint cache +**/.stylelintcache + +# Microbundle cache +**/.rpt2_cache +**/.rts2_cache_cjs +**/.rts2_cache_es +**/.rts2_cache_umd + +# Optional REPL history +**/.node_repl_history + +# Output of 'npm pack' +**/*.tgz + +# Yarn Integrity file +**/.yarn-integrity + +# dotenv environment variable files +**/.env +**/.env.development.local +**/.env.test.local +**/.env.production.local +**/.env.local + +# parcel-bundler cache (https://parceljs.org/) +**/.cache +**/.parcel-cache + +# Next.js build output +**/.next +**/out + +# Nuxt.js build / generate output +**/.nuxt +**/dist + +# Gatsby files +**/.cache +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +**/.vuepress/dist + +# vuepress v2.x temp and cache directory +**/.temp + +# Docusaurus cache and generated files +**/.docusaurus + +# Serverless directories +**/.serverless + +# FuseBox cache +**/.fusebox + +# DynamoDB Local files +**/.dynamodb + +# TernJS port file +**/.tern-port + +# Stores VSCode versions used for testing VSCode extensions +**/.vscode-test + +# yarn v2 +**/.yarn/cache +**/.yarn/unplugged +**/.yarn/build-state.yml +**/.yarn/install-state.gz +**/.pnp.* + +### Node Patch ### +# Serverless Webpack directories +**/.webpack + +# Optional stylelint cache + +# SvelteKit build / generate output +**/.svelte-kit + +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +**/.idea/**/workspace.xml +**/.idea/**/tasks.xml +**/.idea/**/usage.statistics.xml +**/.idea/**/dictionaries +**/.idea/**/shelf + +# AWS User-specific +**/.idea/**/aws.xml + +# Generated files +**/.idea/**/contentModel.xml + +# Sensitive or high-churn files +**/.idea/**/dataSources +**/.idea/**/dataSources.ids +**/.idea/**/dataSources.local.xml +**/.idea/**/sqlDataSources.xml +**/.idea/**/dynamic.xml +**/.idea/**/uiDesigner.xml +**/.idea/**/dbnavigator.xml + +# Gradle +**/.idea/**/gradle.xml +**/.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +**/cmake-build-* + +# Mongo Explorer plugin +**/.idea/**/mongoSettings.xml + +# File-based project format +**/*.iws + +# IntelliJ +**/out + +# mpeltonen/sbt-idea plugin +**/.idea_modules + +# JIRA plugin +**/atlassian-ide-plugin.xml + +# Cursive Clojure plugin +**/.idea/replstate.xml + +# SonarLint plugin +**/.idea/sonarlint + +# Crashlytics plugin (for Android Studio and IntelliJ) +**/com_crashlytics_export_strings.xml +**/crashlytics.properties +**/crashlytics-build.properties +**/fabric.properties + +# Editor-based Rest Client +**/.idea/httpRequests + +# Android studio 3.1+ serialized cache file +**/.idea/caches/build_file_checksums.ser + +### PyCharm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +**/.idea/**/sonarlint + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +**/.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +**/.idea/**/markdown-navigator.xml +**/.idea/**/markdown-navigator-enh.xml +**/.idea/**/markdown-navigator + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +**/.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +**/.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +**/.idea/**/azureSettings.xml + +### Python ### +# Byte-compiled / optimized / DLL files +**/__pycache__ +**/*.py[cod] +**/*$py.class + +# C extensions +**/*.so + +# Distribution / packaging +**/.Python +**/build +**/develop-eggs +**/downloads +**/eggs +**/.eggs +**/lib +**/lib64 +**/parts +**/sdist +**/var +**/wheels +**/share/python-wheels +**/*.egg-info +**/.installed.cfg +**/*.egg +**/MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +**/*.manifest +**/*.spec + +# Installer logs +**/pip-log.txt +**/pip-delete-this-directory.txt + +# Unit test / coverage reports +**/htmlcov +**/.tox +**/.nox +**/.coverage +**/.coverage.* +**/nosetests.xml +**/coverage.xml +**/*.cover +**/*.py,cover +**/.hypothesis +**/.pytest_cache +**/cover + +# Translations +**/*.mo +**/*.pot + +# Django stuff: +**/local_settings.py +**/db.sqlite3 +**/db.sqlite3-journal + +# Flask stuff: +**/instance +**/.webassets-cache + +# Scrapy stuff: +**/.scrapy + +# Sphinx documentation +**/docs/_build + +# PyBuilder +**/.pybuilder +**/target + +# Jupyter Notebook +**/.ipynb_checkpoints + +# IPython +**/profile_default +**/ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +**/.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +**/__pypackages__ + +# Celery stuff +**/celerybeat-schedule +**/celerybeat.pid + +# SageMath parsed files +**/*.sage.py + +# Environments +**/.venv +**/env +**/venv +**/ENV +**/env.bak +**/venv.bak + +# Spyder project settings +**/.spyderproject +**/.spyproject + +# Rope project settings +**/.ropeproject + +# mkdocs documentation +site + +# mypy +**/.mypy_cache +**/.dmypy.json +**/dmypy.json + +# Pyre type checker +**/.pyre + +# pytype static type analyzer +**/.pytype + +# Cython debug symbols +**/cython_debug + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +**/poetry.toml + +# ruff +**/.ruff_cache + +# LSP config files +**/pyrightconfig.json + +### SublimeText ### +# Cache files for Sublime Text +**/*.tmlanguage.cache +**/*.tmPreferences.cache +**/*.stTheme.cache + +# Workspace files are user-specific +**/*.sublime-workspace + +# Project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using Sublime Text +# *.sublime-project + +# SFTP configuration file +**/sftp-config.json +**/sftp-config-alt*.json + +# Package control specific files +**/Package Control.last-run +**/Package Control.ca-list +**/Package Control.ca-bundle +**/Package Control.system-ca-bundle +**/Package Control.cache +**/Package Control.ca-certs +**/Package Control.merged-ca-bundle +**/Package Control.user-ca-bundle +**/oscrypto-ca-bundle.crt +**/bh_unicode_properties.cache + +# Sublime-github package stores a github token in this file +# https://packagecontrol.io/packages/sublime-github +**/GitHub.sublime-settings + +### Vim ### +# Swap +**/[._]*.s[a-v][a-z] +!**/*.svg # comment out if you don't need vector files +**/[._]*.sw[a-p] +**/[._]s[a-rt-v][a-z] +**/[._]ss[a-gi-z] +**/[._]sw[a-p] + +# Session +**/Session.vim +**/Sessionx.vim + +# Temporary +**/.netrwhist +# Auto-generated tag files +**/tags +# Persistent undo +**/[._]*.un~ + +### VisualStudioCode ### +**/.vscode/* +!**/.vscode/settings.json +!**/.vscode/tasks.json +!**/.vscode/launch.json +!**/.vscode/extensions.json +!**/.vscode/*.code-snippets + +# Local History for Visual Studio Code +**/.history + +# Built Visual Studio Code Extensions +**/*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +**/.history +**/.ionide + +### VisualStudio ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +**/*.rsuser +**/*.suo +**/*.user +**/*.userosscache +**/*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +**/*.userprefs + +# Mono auto generated files +**/mono_crash.* + +# Build results +**/[Dd]ebug +**/[Dd]ebugPublic +**/[Rr]elease +**/[Rr]eleases +**/x64 +**/x86 +**/[Ww][Ii][Nn]32 +**/[Aa][Rr][Mm] +**/[Aa][Rr][Mm]64 +**/bld +**/[Bb]in +**/[Oo]bj +**/[Ll]og +**/[Ll]ogs + +# Visual Studio 2015/2017 cache/options directory +**/.vs +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +**/Generated\ Files + +# MSTest test Results +**/[Tt]est[Rr]esult* +**/[Bb]uild[Ll]og.* + +# NUnit +**/*.VisualState.xml +**/TestResult.xml +**/nunit-*.xml + +# Build Results of an ATL Project +**/[Dd]ebugPS +**/[Rr]eleasePS +**/dlldata.c + +# Benchmark Results +**/BenchmarkDotNet.Artifacts + +# .NET Core +**/project.lock.json +**/project.fragment.lock.json +**/artifacts + +# ASP.NET Scaffolding +**/ScaffoldingReadMe.txt + +# StyleCop +**/StyleCopReport.xml + +# Files built by Visual Studio +**/*_i.c +**/*_p.c +**/*_h.h +**/*.ilk +**/*.meta +**/*.obj +**/*.iobj +**/*.pch +**/*.pdb +**/*.ipdb +**/*.pgc +**/*.pgd +**/*.rsp +**/*.sbr +**/*.tlb +**/*.tli +**/*.tlh +**/*.tmp +**/*.tmp_proj +**/*_wpftmp.csproj +**/*.tlog +**/*.vspscc +**/*.vssscc +**/.builds +**/*.pidb +**/*.svclog +**/*.scc + +# Chutzpah Test files +**/_Chutzpah* + +# Visual C++ cache files +**/ipch +**/*.aps +**/*.ncb +**/*.opendb +**/*.opensdf +**/*.cachefile +**/*.VC.db +**/*.VC.VC.opendb + +# Visual Studio profiler +**/*.psess +**/*.vsp +**/*.vspx +**/*.sap + +# Visual Studio Trace Files +**/*.e2e + +# TFS 2012 Local Workspace +**/$tf + +# Guidance Automation Toolkit +**/*.gpState + +# ReSharper is a .NET coding add-in +**/_ReSharper* +**/*.[Rr]e[Ss]harper +**/*.DotSettings.user + +# TeamCity is a build add-in +**/_TeamCity* + +# DotCover is a Code Coverage Tool +**/*.dotCover + +# AxoCover is a Code Coverage Tool +**/.axoCover/* +!**/.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +**/coverage*.json +**/coverage*.xml +**/coverage*.info + +# Visual Studio code coverage results +**/*.coverage +**/*.coveragexml + +# NCrunch +**/_NCrunch_* +**/.*crunch*.local.xml +**/nCrunchTemp_* + +# MightyMoose +**/*.mm.* +**/AutoTest.Net + +# Web workbench (sass) +**/.sass-cache + +# Installshield output folder +**/[Ee]xpress + +# DocProject is a documentation generator add-in +**/DocProject/buildhelp +**/DocProject/Help/*.HxT +**/DocProject/Help/*.HxC +**/DocProject/Help/*.hhc +**/DocProject/Help/*.hhk +**/DocProject/Help/*.hhp +**/DocProject/Help/Html2 +**/DocProject/Help/html + +# Click-Once directory +**/publish + +# Publish Web Output +**/*.[Pp]ublish.xml +**/*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +**/*.pubxml +**/*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +**/PublishScripts + +# NuGet Packages +**/*.nupkg +# NuGet Symbol Packages +**/*.snupkg +# The packages folder can be ignored because of Package Restore +**/**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/**/[Pp]ackages/build +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +**/*.nuget.props +**/*.nuget.targets + +# Microsoft Azure Build Output +**/csx +**/*.build.csdef + +# Microsoft Azure Emulator +**/ecf +**/rcf + +# Windows Store app package directories and files +**/AppPackages +**/BundleArtifacts +**/Package.StoreAssociation.xml +**/_pkginfo.txt +**/*.appx +**/*.appxbundle +**/*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +**/*.[Cc]ache +# but keep track of directories ending in .cache +!**/?*.[Cc]ache + +# Others +**/ClientBin +**/~$* +**/*.dbmdl +**/*.dbproj.schemaview +**/*.jfm +**/*.pfx +**/*.publishsettings +**/orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +**/Generated_Code + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +**/_UpgradeReport_Files +**/Backup* +**/UpgradeLog*.XML +**/UpgradeLog*.htm +**/ServiceFabricBackup +**/*.rptproj.bak + +# SQL Server files +**/*.mdf +**/*.ldf +**/*.ndf + +# Business Intelligence projects +**/*.rdl.data +**/*.bim.layout +**/*.bim_*.settings +**/*.rptproj.rsuser +**/*- [Bb]ackup.rdl +**/*- [Bb]ackup ([0-9]).rdl +**/*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +**/FakesAssemblies + +# GhostDoc plugin setting file +**/*.GhostDoc.xml + +# Node.js Tools for Visual Studio +**/.ntvs_analysis.dat + +# Visual Studio 6 build log +**/*.plg + +# Visual Studio 6 workspace options file +**/*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +**/*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +**/*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +**/*.dsw +**/*.dsp + +# Visual Studio 6 technical files + +# Visual Studio LightSwitch build output +**/**/*.HTMLClient/GeneratedArtifacts +**/**/*.DesktopClient/GeneratedArtifacts +**/**/*.DesktopClient/ModelManifest.xml +**/**/*.Server/GeneratedArtifacts +**/**/*.Server/ModelManifest.xml +**/_Pvt_Extensions + +# Paket dependency manager +**/.paket/paket.exe +**/paket-files + +# FAKE - F# Make +**/.fake + +# CodeRush personal settings +**/.cr/personal + +# Python Tools for Visual Studio (PTVS) +**/*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +**/*.tss + +# Telerik's JustMock configuration file +**/*.jmconfig + +# BizTalk build output +**/*.btp.cs +**/*.btm.cs +**/*.odx.cs +**/*.xsd.cs + +# OpenCover UI analysis results +**/OpenCover + +# Azure Stream Analytics local run output +**/ASALocalRun + +# MSBuild Binary and Structured Log +**/*.binlog + +# NVidia Nsight GPU debugger configuration file +**/*.nvuser + +# MFractors (Xamarin productivity tool) working folder +**/.mfractor + +# Local History for Visual Studio +**/.localhistory + +# Visual Studio History (VSHistory) files +**/.vshistory + +# BeatPulse healthcheck temp database +**/healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +**/MigrationBackup + +# Ionide (cross platform F# VS Code tools) working folder +**/.ionide + +# Fody - auto-generated XML schema +**/FodyWeavers.xsd + +# VS Code files for those working on multiple tools +**/*.code-workspace + +# Local History for Visual Studio Code + +# Windows Installer files from build outputs +**/*.cab +**/*.msi +**/*.msix +**/*.msm +**/*.msp + +# JetBrains Rider +**/*.sln.iml + +### VisualStudio Patch ### +# Additional files built by Visual Studio + +# End of https://www.toptal.com/developers/gitignore/api/vim,node,data,emacs,python,pycharm,executable,sublimetext,visualstudio,visualstudiocode +**/database.db +**/database.db +**/database.db +**/.markata.cache +**/database.sqlite + +# flyctl launch added from .pytest_cache/.gitignore +# Created by pytest automatically. +.pytest_cache/**/* + +# flyctl launch added from .ruff_cache/.gitignore +.ruff_cache/**/* +fly.toml diff --git a/.gitignore b/.gitignore index 9c4e139..1683c90 100644 --- a/.gitignore +++ b/.gitignore @@ -967,3 +967,5 @@ database.db database.db .markata.cache database.sqlite +.env.dev +.env.dev.docker diff --git a/d3.py b/d3.py new file mode 100644 index 0000000..3b41786 --- /dev/null +++ b/d3.py @@ -0,0 +1,70 @@ +import sqlite3 + +from jinja2 import Environment, FileSystemLoader + + +def get_tables_and_columns(conn): + cursor = conn.cursor() + cursor.execute("SELECT name FROM sqlite_master WHERE type='table';") + tables = [ + { + "name": table[0], + "columns": get_columns(conn, table[0]), + "foreign_keys": get_foreign_keys(conn, table[0]), + } + for table in cursor.fetchall() + ] + return tables + + +def get_columns(conn, table_name): + cursor = conn.cursor() + cursor.execute(f"PRAGMA table_info({table_name});") + columns = [row[1] for row in cursor.fetchall()] + return columns + + +def get_foreign_keys(conn, table_name): + cursor = conn.cursor() + cursor.execute(f"PRAGMA foreign_key_list({table_name});") + foreign_keys = [ + {"id": row[0], "from": row[3], "to_table": row[2], "to": row[4]} + for row in cursor.fetchall() + ] + return foreign_keys + + +def generate_links(tables): + links = [] + for t_index, table in enumerate(tables): + for fk in table["foreign_keys"]: + target_index = next( + i for i, target in enumerate(tables) if target["name"] == fk["to_table"] + ) + source_y = 40 + table["columns"].index(fk["from"]) * 20 + target_y = 40 + tables[target_index]["columns"].index(fk["to"]) * 20 + links.append( + { + "source": {"x": 50 + t_index * 150 + 120, "y": 50 + source_y}, + "target": {"x": 50 + target_index * 150, "y": 50 + target_y}, + } + ) + return links + + +def generate_er_diagram(database_path): + conn = sqlite3.connect(database_path) + tables = get_tables_and_columns(conn) + links = [] # Currently, we won't extract relationships + links = generate_links(tables) + + env = Environment(loader=FileSystemLoader("templates")) + template = env.get_template("er_diagram.html") + + with open("index.html", "w") as f: + f.write(template.render(tables=tables, links=links)) + + +if __name__ == "__main__": + db_path = "database.db" + generate_er_diagram(db_path) diff --git a/database.md b/database.md new file mode 100644 index 0000000..481c4a0 --- /dev/null +++ b/database.md @@ -0,0 +1,72 @@ +![ER Diagram](er_diagram.png) + +--- + +## Table: learn_sql_model_alembic_version + +### First 5 rows + +| version_num | +|-------------| +| f48730a783a5 | + +### Columns + +| Column Name | Type | Foreign Key | Example Value | +|-------------|------|-------------|---------------| +| version_num | VARCHAR(32) | | | | + +### Records Count + +The table learn_sql_model_alembic_version contains 1 records. + +--- + +## Table: pet + +### First 5 rows + +| name | birthday | id | +|------|----------|----| + +### Columns + +| Column Name | Type | Foreign Key | Example Value | +|-------------|------|-------------|---------------| +| name | VARCHAR | | | | +| birthday | DATETIME | | | | +| id | INTEGER | | | | + +### Records Count + +The table pet contains 0 records. + +--- + +## Table: hero + +### First 5 rows + +| name | secret_name | x | y | size | age | shoe_size | pet_id | id | +|------|-------------|---|---|------|-----|-----------|--------|----| + +### Columns + +| Column Name | Type | Foreign Key | Example Value | +|-------------|------|-------------|---------------| +| name | VARCHAR | | | | +| secret_name | VARCHAR | | | | +| x | INTEGER | | | | +| y | INTEGER | | | | +| size | INTEGER | | | | +| age | INTEGER | | | | +| shoe_size | INTEGER | | | | +| pet_id | INTEGER | pet.id | | | +| id | INTEGER | | | | + +### Records Count + +The table hero contains 0 records. + +--- + diff --git a/er_diagram.png b/er_diagram.png new file mode 100644 index 0000000..afcf6e0 Binary files /dev/null and b/er_diagram.png differ diff --git a/im.png b/im.png new file mode 100644 index 0000000..9196f21 Binary files /dev/null and b/im.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..bb40a04 --- /dev/null +++ b/index.html @@ -0,0 +1,129 @@ + + + + + + ER Diagram + + + + + + +
+ + + + \ No newline at end of file diff --git a/learn_sql_model/cli/model.py b/learn_sql_model/cli/model.py index d622821..786daff 100644 --- a/learn_sql_model/cli/model.py +++ b/learn_sql_model/cli/model.py @@ -1,15 +1,15 @@ from pathlib import Path from typing import Annotated -import copier +# import copier import typer from learn_sql_model.cli.common import verbose_callback from learn_sql_model.config import get_config from learn_sql_model.optional import _optional_import_ -alembic = _optional_import_('alembic', group='manage') -Config = _optional_import_('alembic.config', 'Config', group='manage') +alembic = _optional_import_("alembic", group="manage") +Config = _optional_import_("alembic.config", "Config", group="manage") model_app = typer.Typer() @@ -94,5 +94,4 @@ def populate( callback=verbose_callback, help="show the log messages", ), -): - ... +): ... diff --git a/learn_sql_model/game/debug.py b/learn_sql_model/game/debug.py new file mode 100644 index 0000000..8e01bf3 --- /dev/null +++ b/learn_sql_model/game/debug.py @@ -0,0 +1,25 @@ +import pygame + + +class Debug: + def __init__(self, game): + self.game = game + self.is_open = False + self.debounce = False + + def handle_events(self, events): + for event in events: + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_F3 and not self.debounce: + self.is_open = not self.is_open + self.debounce = True + if event.type == pygame.KEYUP: + if event.key == pygame.K_F3: + self.debounce = False + + def render(self): + if self.is_open: + text = self.game.font.render( + str(int(self.game.clock.get_fps())) + " fps", True, (255, 255, 255) + ) + self.game.screen.blit(text, (20, 20)) diff --git a/learn_sql_model/game/game.py b/learn_sql_model/game/game.py index ffb0a0b..742d8f5 100644 --- a/learn_sql_model/game/game.py +++ b/learn_sql_model/game/game.py @@ -5,6 +5,7 @@ from websocket import create_connection from learn_sql_model.config import get_config from learn_sql_model.console import console +from learn_sql_model.game.debug import Debug from learn_sql_model.game.light import Light from learn_sql_model.game.map import Map from learn_sql_model.game.menu import Menu @@ -20,7 +21,8 @@ config = get_config() class Client: def __init__(self): - self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN) + # self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN) + self.screen = pygame.display.set_mode((1280, 720)) pygame.display.set_caption("Learn SQL Model") self.clock = pygame.time.Clock() self.running = True @@ -38,8 +40,11 @@ class Client: self.font = pygame.font.SysFont("", 25) self.joysticks = {} self.darkness = pygame.Surface( - (self.screen.get_width(), self.screen.get_height()) + (self.screen.get_width(), self.screen.get_height()), + pygame.SRCALPHA, + 32, ) + self.debug = Debug(self) atexit.register(self.quit) @@ -75,11 +80,11 @@ class Client: console.print("update") self.update() console.print("render") + self.render() time = self.clock.tick(60) self.elapsed = time / 100 self.ticks += 1 - # Console().print(self.clock.get_fps()) console.print(f"time: {time}") console.print(f"ticks: {self.ticks}") if profiler: @@ -98,9 +103,11 @@ class Client: self.screen.fill((0, 0, 0)) self.map.render() self.player.render() - light_level = 0 - self.darkness.fill((light_level, light_level, light_level)) - self.light.render() + + if self.ticks % 1 == 0 or self.ticks == 0: + light_level = 0 + self.darkness.fill((light_level, light_level, light_level)) + self.light.render() self.screen.blit( self.darkness, (0, 0), @@ -109,11 +116,13 @@ class Client: # update the screen self.menu.render() + self.debug.render() pygame.display.flip() def handle_events(self): self.events = pygame.event.get() self.menu.handle_events(self.events) + self.debug.handle_events(self.events) self.player.handle_events() for event in self.events: if event.type == pygame.QUIT: diff --git a/learn_sql_model/game/light.py b/learn_sql_model/game/light.py index 9000bd5..abaa861 100644 --- a/learn_sql_model/game/light.py +++ b/learn_sql_model/game/light.py @@ -1,34 +1,133 @@ +import bisect + +from PIL import Image, ImageFilter + from learn_sql_model.optional import _optional_import_ pygame = _optional_import_("pygame", group="game") +def rot_center(image, angle): + """rotate an image while keeping its center and size""" + orig_rect = image.get_rect() + rot_image = pygame.transform.rotate(image, angle) + rot_rect = orig_rect.copy() + rot_rect.center = rot_image.get_rect().center + rot_image = rot_image.subsurface(rot_rect).copy() + return rot_image + + class Light: def __init__(self, game): self.game = game self.surf = pygame.Surface( - (self.game.screen.get_width(), self.game.screen.get_height()) + (self.game.screen.get_width(), self.game.screen.get_height()), + pygame.SRCALPHA, + 32, ) - # pil_image = Image.new("RGBA", (1000, 500)) - # pil_draw = ImageDraw.Draw(pil_image) - # pil_draw.pieslice((-1500, -100, 1000, 600), 340, 20, fill=(255, 250, 205)) - # pil_image = pil_image.filter(ImageFilter.GaussianBlur(radius=5)) + self.surf.set_colorkey((0, 0, 0)) + self.pre_render() - # mode = pil_image.mode - # size = pil_image.size - # data = pil_image.tobytes() + def pre_render(self): - # self.image = pygame.image.fromstring(data, size, mode) + # self.lights = {} + # for deg in range(-360, 360, 20): + # print("loading light", deg) + # self.lights[deg] = pygame.image.load( + # f"lights/light-{deg}.png" + # ).convert_alpha() + # return - # for r in range(-25, 25): - # _v = v.rotate(r) - # pygame.draw.line( - # self.game.screen, - # (255, 250, 205), - # (0, 50), - # (0 + _v.x, self.game.player.hero.y + _v.y), - # 50, - # ) + light_surf = pygame.Surface( + ( + self.game.player.hero.flashlight_strength * 3, + self.game.player.hero.flashlight_strength * 3, + ), + pygame.SRCALPHA, + 32, + ) + + v = pygame.math.Vector2(0, 1) + v.scale_to_length(self.game.player.hero.flashlight_strength) + for r in range(-90 - 25, -90 + 25): + _v = v.rotate(r) + pygame.draw.line( + light_surf, + (255, 250, 205), + (light_surf.get_width() / 2, light_surf.get_height() / 2), + ( + light_surf.get_width() / 2 + _v.x, + light_surf.get_height() / 2 + _v.y, + ), + 50, + ) + pygame.draw.circle( + light_surf, + (255, 250, 205), + (light_surf.get_width() / 2, light_surf.get_height() / 2), + self.game.player.hero.lanturn_strength, + ) + + light_surf_pil = Image.frombytes( + "RGBA", + (light_surf.get_width(), light_surf.get_height()), + pygame.image.tostring(light_surf, "RGBA", False), + ) + light_surf_blur = light_surf_pil.filter(ImageFilter.GaussianBlur(radius=100)) + light_surf = pygame.image.fromstring( + light_surf_blur.tobytes(), + (light_surf.get_width(), light_surf.get_height()), + "RGBA", + ).convert_alpha() + + pygame.draw.circle( + light_surf, + (255, 250, 205), + (light_surf.get_width() / 2, light_surf.get_height() / 2), + self.game.player.hero.lanturn_strength, + ) + + light_surf_pil = Image.frombytes( + "RGBA", + (light_surf.get_width(), light_surf.get_height()), + pygame.image.tostring(light_surf, "RGBA", False), + ) + light_surf_blur = light_surf_pil.filter(ImageFilter.GaussianBlur(radius=50)) + light_surf = pygame.image.fromstring( + light_surf_blur.tobytes(), + (light_surf.get_width(), light_surf.get_height()), + "RGBA", + ).convert_alpha() + + pygame.draw.circle( + light_surf, + (255, 250, 205), + (light_surf.get_width() / 2, light_surf.get_height() / 2), + self.game.player.hero.lanturn_strength, + ) + + light_surf_pil = Image.frombytes( + "RGBA", + (light_surf.get_width(), light_surf.get_height()), + pygame.image.tostring(light_surf, "RGBA", False), + ) + light_surf_blur = light_surf_pil.filter(ImageFilter.GaussianBlur(radius=20)) + light_surf = pygame.image.fromstring( + light_surf_blur.tobytes(), + (light_surf.get_width(), light_surf.get_height()), + "RGBA", + ).convert_alpha() + + self.light_surf = light_surf + self.light_surf.set_colorkey((0, 0, 0)) + + self.lights = { + deg: pygame.transform.rotate(self.light_surf, deg - 90) + for deg in range(-360, 360, 20) + } + + for deg, light in self.lights.items(): + pygame.image.save(light, f"lights/light-{deg}.png") def render(self): self.surf.fill((0, 0, 0)) @@ -37,68 +136,84 @@ class Light: mx - self.game.player.hero.x, my - self.game.player.hero.y ) v.scale_to_length(self.game.player.hero.flashlight_strength) - self.game.player.hero.flashlight_angle = v.angle_to(pygame.math.Vector2(0, 1)) - - for r in range(-25, 25): - _v = v.rotate(r) - pygame.draw.line( - self.surf, - (255, 250, 205), - (self.game.player.hero.x, self.game.player.hero.y), - (self.game.player.hero.x + _v.x, self.game.player.hero.y + _v.y), - 50, - ) + self.game.player.hero.flashlight_angle = v.angle_to(pygame.math.Vector2(1, 0)) for other in self.game.player.others.__root__: if other.id == self.game.player.hero.id: continue - v = pygame.math.Vector2(0, 1) - v = v.rotate(-other.flashlight_angle) - v.scale_to_length(other.flashlight_strength) - for r in range(-25, 25): - _v = v.rotate(r) - pygame.draw.line( - self.surf, - (255, 250, 205), - (other.x, other.y), - (other.x + _v.x, other.y + _v.y), - 50, + + light_index = list(self.lights.keys())[ + bisect.bisect_left( + list(self.lights.keys()), + other.flashlight_angle + 90, ) - pygame.draw.circle( - self.surf, - (255, 250, 205), - (other.x, other.y), - other.lanturn_strength, + ] + + my_light = self.lights[light_index] + self.surf.blit( + my_light, + ( + other.x - my_light.get_width() / 2, + other.y - my_light.get_height() / 2, + ), ) - # draw a circle - pygame.draw.circle( - self.surf, - (255, 250, 205), - (self.game.player.hero.x, self.game.player.hero.y), - self.game.player.hero.lanturn_strength, + light_index = list(self.lights.keys())[ + bisect.bisect_left( + list(self.lights.keys()), + self.game.player.hero.flashlight_angle + 90, + ) + ] + + my_light = self.lights[light_index] + self.surf.blit( + my_light, + ( + self.game.player.hero.x - my_light.get_width() / 2, + self.game.player.hero.y - my_light.get_height() / 2, + ), ) + # for r in range(-25, 25): + # _v = v.rotate(r) + # pygame.draw.line( + # self.surf, + # (255, 250, 205), + # (self.game.player.hero.x, self.game.player.hero.y), + # (self.game.player.hero.x + _v.x, self.game.player.hero.y + _v.y), + # 50, + # ) + # # draw a circle + # pygame.draw.circle( + # self.surf, + # (255, 250, 205), + # (self.game.player.hero.x, self.game.player.hero.y), + # self.game.player.hero.lanturn_strength, + # ) + + # for other in self.game.player.others.__root__: + # if other.id == self.game.player.hero.id: + # continue + # v = pygame.math.Vector2(0, 1) + # v = v.rotate(-other.flashlight_angle) + # v.scale_to_length(other.flashlight_strength) + # for r in range(-25, 25): + # _v = v.rotate(r) + # pygame.draw.line( + # self.surf, + # (255, 250, 205), + # (other.x, other.y), + # (other.x + _v.x, other.y + _v.y), + # 50, + # ) + # pygame.draw.circle( + # self.surf, + # (255, 250, 205), + # (other.x, other.y), + # other.lanturn_strength, + # ) + self.game.darkness.blit( self.surf, (0, 0), ) - - -def render_flashlight(light, strength, angle): - - # self.darkness.blit( - # pygame.transform.smoothscale( - # self.spot, [self.light_power, self.light_power] - # ), - # (self.x - self.light_power / 2, self.y - self.light_power / 2), - # ) - for r in range(-25, 25): - _v = v.rotate(r) - pygame.draw.line( - light, - (255, 250, 205), - (self.game.player.hero.x, self.game.player.hero.y), - (self.game.player.hero.x + _v.x, self.game.player.hero.y + _v.y), - 50, - ) diff --git a/learn_sql_model/game/map.py b/learn_sql_model/game/map.py index 7bb04c9..e792056 100644 --- a/learn_sql_model/game/map.py +++ b/learn_sql_model/game/map.py @@ -39,6 +39,33 @@ class Map: # try to load the map from map.png try: self.surf = pygame.image.load("map.png").convert_alpha() + + # self.surf_pil = Image.frombytes( + # "RGBA", + # (self.surf.get_width(), self.surf.get_height()), + # pygame.image.tostring(self.surf, "RGBA", False), + # ) + # self.surf_blur = ( + # self.surf_pil.filter( + # ImageFilter.SMOOTH_MORE(), + # ) + # .filter(ImageFilter.SMOOTH_MORE()) + # .filter(ImageFilter.SMOOTH_MORE()) + # .filter(ImageFilter.SMOOTH_MORE()) + # .filter(ImageFilter.SMOOTH_MORE()) + # .filter(ImageFilter.SMOOTH_MORE()) + # # sharpen + # .filter(ImageFilter.UnsharpMask(radius=3, percent=100, threshold=3)) + # .filter(ImageFilter.UnsharpMask(radius=3, percent=100, threshold=3)) + # .filter(ImageFilter.UnsharpMask(radius=3, percent=100, threshold=3)) + # ) + + # self.surf = pygame.image.fromstring( + # self.surf_blur.tobytes(), + # (self.surf.get_width(), self.surf.get_height()), + # "RGBA", + # ).convert_alpha() + except FileNotFoundError: self.pre_draw() diff --git a/learn_sql_model/game/menu.py b/learn_sql_model/game/menu.py index 565c305..c3c67f5 100644 --- a/learn_sql_model/game/menu.py +++ b/learn_sql_model/game/menu.py @@ -1,6 +1,7 @@ from typing import Callable, Tuple from pydantic import BaseModel + from learn_sql_model.optional import _optional_import_ pygame = _optional_import_("pygame", group="game") @@ -113,7 +114,7 @@ class Menu: def handle_events(self, events): self.hamburger.handle_events(self, events) for event in events: - if event.type == pygame.MOUSEBUTTONDOWN: + if event.type == pygame.MOUSEBUTTONDOWN and self.is_menu_open: if event.button == 1: # Left mouse button self.handle_click() diff --git a/learn_sql_model/game/player.py b/learn_sql_model/game/player.py index 508c75a..c7e1a79 100644 --- a/learn_sql_model/game/player.py +++ b/learn_sql_model/game/player.py @@ -33,9 +33,13 @@ class Player: self.speed = 10 self.max_speed = 10 self.image = pygame.image.load("creeper.png").convert_alpha() + self.pet_image = pygame.image.load("pet.png").convert_alpha() self.image = pygame.transform.scale( self.image, (self.hero.size, self.hero.size) ) + self.pet_image = pygame.transform.scale( + self.pet_image, (self.hero.size/1.5, self.hero.size/2) + ) self.x_last = self.x self.y_last = self.y self.hitbox_surface = pygame.Surface((self.width, self.height)) @@ -204,16 +208,16 @@ class Player: self.x_last = self.hero.x self.y_last = self.hero.y - # if self.game.ticks % 1 == 0 or self.game.ticks == 0: - console.print("updating") - update = HeroUpdate(**self.hero.dict(exclude_unset=True)) - console.print(update) - self.game.ws.send(update.json()) - console.print("sent") + if self.game.ticks % 60 == 0 or self.game.ticks == 0: + console.print("updating") + update = HeroUpdate(**self.hero.dict(exclude_unset=True)) + console.print(update) + self.game.ws.send(update.json()) + console.print("sent") - raw_heros = self.game.ws.recv() - console.print(raw_heros) - self.others = Heros.parse_raw(raw_heros) + raw_heros = self.game.ws.recv() + console.print(raw_heros) + self.others = Heros.parse_raw(raw_heros) def draw(self): self.move() @@ -242,6 +246,10 @@ class Player: self.image, (self.hero.x - self.hero.size / 2, self.hero.y - self.hero.size / 2), ) + self.game.screen.blit( + self.pet_image, + (self.hero.x + self.hero.size / 2, self.hero.y - self.hero.size / 2), + ) # pygame.draw.circle( # self.game.screen, (0, 0, 255), (self.hero.x, self.hero.y), self.hero.size diff --git a/lights/light--100.png b/lights/light--100.png new file mode 100644 index 0000000..de46434 Binary files /dev/null and b/lights/light--100.png differ diff --git a/lights/light--120.png b/lights/light--120.png new file mode 100644 index 0000000..963152f Binary files /dev/null and b/lights/light--120.png differ diff --git a/lights/light--140.png b/lights/light--140.png new file mode 100644 index 0000000..cda76ae Binary files /dev/null and b/lights/light--140.png differ diff --git a/lights/light--160.png b/lights/light--160.png new file mode 100644 index 0000000..3643c9e Binary files /dev/null and b/lights/light--160.png differ diff --git a/lights/light--180.png b/lights/light--180.png new file mode 100644 index 0000000..82f9e33 Binary files /dev/null and b/lights/light--180.png differ diff --git a/lights/light--20.png b/lights/light--20.png new file mode 100644 index 0000000..3e14e6b Binary files /dev/null and b/lights/light--20.png differ diff --git a/lights/light--200.png b/lights/light--200.png new file mode 100644 index 0000000..6490af9 Binary files /dev/null and b/lights/light--200.png differ diff --git a/lights/light--220.png b/lights/light--220.png new file mode 100644 index 0000000..a59b646 Binary files /dev/null and b/lights/light--220.png differ diff --git a/lights/light--240.png b/lights/light--240.png new file mode 100644 index 0000000..0f036b7 Binary files /dev/null and b/lights/light--240.png differ diff --git a/lights/light--260.png b/lights/light--260.png new file mode 100644 index 0000000..7033ac0 Binary files /dev/null and b/lights/light--260.png differ diff --git a/lights/light--280.png b/lights/light--280.png new file mode 100644 index 0000000..42509bf Binary files /dev/null and b/lights/light--280.png differ diff --git a/lights/light--300.png b/lights/light--300.png new file mode 100644 index 0000000..0c63757 Binary files /dev/null and b/lights/light--300.png differ diff --git a/lights/light--320.png b/lights/light--320.png new file mode 100644 index 0000000..4112187 Binary files /dev/null and b/lights/light--320.png differ diff --git a/lights/light--340.png b/lights/light--340.png new file mode 100644 index 0000000..9a9bb4d Binary files /dev/null and b/lights/light--340.png differ diff --git a/lights/light--360.png b/lights/light--360.png new file mode 100644 index 0000000..553fc60 Binary files /dev/null and b/lights/light--360.png differ diff --git a/lights/light--40.png b/lights/light--40.png new file mode 100644 index 0000000..c142c08 Binary files /dev/null and b/lights/light--40.png differ diff --git a/lights/light--60.png b/lights/light--60.png new file mode 100644 index 0000000..8965054 Binary files /dev/null and b/lights/light--60.png differ diff --git a/lights/light--80.png b/lights/light--80.png new file mode 100644 index 0000000..9ed81e6 Binary files /dev/null and b/lights/light--80.png differ diff --git a/lights/light-0.png b/lights/light-0.png new file mode 100644 index 0000000..553fc60 Binary files /dev/null and b/lights/light-0.png differ diff --git a/lights/light-100.png b/lights/light-100.png new file mode 100644 index 0000000..7033ac0 Binary files /dev/null and b/lights/light-100.png differ diff --git a/lights/light-120.png b/lights/light-120.png new file mode 100644 index 0000000..0367763 Binary files /dev/null and b/lights/light-120.png differ diff --git a/lights/light-140.png b/lights/light-140.png new file mode 100644 index 0000000..a59b646 Binary files /dev/null and b/lights/light-140.png differ diff --git a/lights/light-160.png b/lights/light-160.png new file mode 100644 index 0000000..6490af9 Binary files /dev/null and b/lights/light-160.png differ diff --git a/lights/light-180.png b/lights/light-180.png new file mode 100644 index 0000000..82f9e33 Binary files /dev/null and b/lights/light-180.png differ diff --git a/lights/light-20.png b/lights/light-20.png new file mode 100644 index 0000000..9a9bb4d Binary files /dev/null and b/lights/light-20.png differ diff --git a/lights/light-200.png b/lights/light-200.png new file mode 100644 index 0000000..3643c9e Binary files /dev/null and b/lights/light-200.png differ diff --git a/lights/light-220.png b/lights/light-220.png new file mode 100644 index 0000000..cda76ae Binary files /dev/null and b/lights/light-220.png differ diff --git a/lights/light-240.png b/lights/light-240.png new file mode 100644 index 0000000..7c35056 Binary files /dev/null and b/lights/light-240.png differ diff --git a/lights/light-260.png b/lights/light-260.png new file mode 100644 index 0000000..de46434 Binary files /dev/null and b/lights/light-260.png differ diff --git a/lights/light-280.png b/lights/light-280.png new file mode 100644 index 0000000..9ed81e6 Binary files /dev/null and b/lights/light-280.png differ diff --git a/lights/light-300.png b/lights/light-300.png new file mode 100644 index 0000000..4c35301 Binary files /dev/null and b/lights/light-300.png differ diff --git a/lights/light-320.png b/lights/light-320.png new file mode 100644 index 0000000..c142c08 Binary files /dev/null and b/lights/light-320.png differ diff --git a/lights/light-340.png b/lights/light-340.png new file mode 100644 index 0000000..3e14e6b Binary files /dev/null and b/lights/light-340.png differ diff --git a/lights/light-40.png b/lights/light-40.png new file mode 100644 index 0000000..4112187 Binary files /dev/null and b/lights/light-40.png differ diff --git a/lights/light-60.png b/lights/light-60.png new file mode 100644 index 0000000..0c63757 Binary files /dev/null and b/lights/light-60.png differ diff --git a/lights/light-80.png b/lights/light-80.png new file mode 100644 index 0000000..42509bf Binary files /dev/null and b/lights/light-80.png differ diff --git a/load_test.py b/load_test.py new file mode 100644 index 0000000..461db8f --- /dev/null +++ b/load_test.py @@ -0,0 +1,18 @@ +from locust import HttpUser, between, task + +from learn_sql_model.factories.hero import HeroFactory +from learn_sql_model.models.hero import HeroCreate + + +class QuickstartUser(HttpUser): + wait_time = between(1, 2) + + @task + def hello_world(self): + self.client.get("/hero/1") + self.client.get("/heros/") + + @task(3) + def create_hero(self): + hero = HeroFactory().build() + HeroCreate(**hero.dict()).post() diff --git a/locustfile.py b/locustfile.py new file mode 100644 index 0000000..4f4f551 --- /dev/null +++ b/locustfile.py @@ -0,0 +1,58 @@ +import random + +from locust import FastHttpUser, task + +from learn_sql_model.config import get_config +from learn_sql_model.factories.hero import HeroFactory +from learn_sql_model.models.hero import HeroCreate, HeroUpdate, Heros + +config = get_config() + + +class QuickstartUser(FastHttpUser): + # wait_time = between(1, 2) + host = "http://localhost:5000" + # host = "https://waylonwalker.com" + + def on_start(self): + self.client.verify = False + + @task(6) + def get_a_hero(self): + # heros = Heros.list() + id = 1 + # id = random.choice(heros.__root__).id + + self.client.get(f"/hero/{id}") + + # @task(2) + # def get_all_hero(self): + # self.client.get("/heros/") + + @task + def create_hero(self): + hero = HeroFactory().build() + hero_create = HeroCreate(**hero.dict()).post() + + self.client.post( + f"{config.api_client.url}/hero/", + json=hero_create.dict(), + ) + + @task(3) + def update_hero(self): + hero = HeroFactory().build() + hero_update = HeroUpdate(id=1, name=hero.name) + + self.client.patch( + "/hero/", + json=hero_update.dict(exclude_none=True), + ) + + @task + def delete_hero(self): + heros = Heros.list() + id = random.choice(heros.__root__).id + self.client.delete( + f"/hero/{id}", + ) diff --git a/map.pkl b/map.pkl new file mode 100644 index 0000000..e69de29 diff --git a/map.png b/map.png new file mode 100644 index 0000000..04809ee Binary files /dev/null and b/map.png differ diff --git a/micro b/micro new file mode 100644 index 0000000..a06df4a Binary files /dev/null and b/micro differ diff --git a/migrations/env.py b/migrations/env.py index 4863934..27494c8 100644 --- a/migrations/env.py +++ b/migrations/env.py @@ -79,7 +79,7 @@ def run_migrations_online() -> None: context.configure( connection=connection, target_metadata=target_metadata, - render_as_batch=False, + render_as_batch=True, version_table=f'{config.get_main_option("project")}_alembic_version', ) diff --git a/notify.py b/notify.py new file mode 100644 index 0000000..8c1178d --- /dev/null +++ b/notify.py @@ -0,0 +1,37 @@ +# # Import smtplib for the actual sending function +# import smtplib +# # Import the email modules we'll need +# from email.mime.text import MIMEText + +# # Open a plain text file for reading. For this example, assume that +# # the text file contains only ASCII characters. +# # with open(textfile, 'rb') as fp: +# # # Create a text/plain message +# # msg = MIMEText(fp.read()) +# msg = MIMEText("hello there", "plain", "utf-8") + +# # me == the sender's email address +# # you == the recipient's email address +# me = "waylon@waylonwalker.com" +# you = "3195728809@msg.fi.google.com" +# msg["Subject"] = "Python SMTP test" +# msg["From"] = me +# msg["To"] = you + +# # Send the message via our own SMTP server, but don't include the +# # envelope header. +# s = smtplib.SMTP("localhost") +# s.sendmail(me, [you], msg.as_string()) +# s.quit() +import requests + +requests.post( + "https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages", + auth=("api", "YOUR_API_KEY"), + data={ + "from": "Excited User ", + "to": ["bar@example.com", "YOU@YOUR_DOMAIN_NAME"], + "subject": "Hello", + "text": "Testing some Mailgun awesomness!", + }, +) diff --git a/pet.png b/pet.png new file mode 100644 index 0000000..b22dd02 Binary files /dev/null and b/pet.png differ diff --git a/pyproject.toml b/pyproject.toml index aba902f..53dbe66 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,7 @@ dependencies = [ "engorgio", "fastapi", "httpx", - "pydantic[dotenv]", + "pydantic<2.0.0", "pyflyby", "pyinstaller", "rich", diff --git a/rect.py b/rect.py new file mode 100644 index 0000000..29def88 --- /dev/null +++ b/rect.py @@ -0,0 +1,23 @@ +import pygame + +pygame.init() + +screen = pygame.display.set_mode((500, 500)) +pygame.display.set_caption("draw a square") + +running = True + +while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + + surface = pygame.Surface((500, 500)) + surface.fill((255, 0, 0)) + + color = (0, 0, 255) + rect = (200, 200, 100, 100) + pygame.draw.rect(surface, color, rect) + + screen.blit(surface, (0, 0)) + pygame.display.flip() diff --git a/templates/er_diagram.html b/templates/er_diagram.html new file mode 100644 index 0000000..5dc1fed --- /dev/null +++ b/templates/er_diagram.html @@ -0,0 +1,129 @@ + + + + + + ER Diagram + + + + + + +
+ + + + diff --git a/tmp.sh b/tmp.sh new file mode 100644 index 0000000..7f36aa4 --- /dev/null +++ b/tmp.sh @@ -0,0 +1,20 @@ +max="$1" +date +echo "url: $2 +rate: $max calls / second" +START=$(date +%s); + +get () { + curl -s -v "$1" 2>&1 | tr '\r\n' '\\n' | awk -v date="$(date +'%r')" '{print $0"\n-----", date}' >> /tmp/perf-test.log +} + +while true +do + echo $(($(date +%s) - START)) | awk '{print int($1/60)":"int($1%60)}' + sleep 1 + + for i in `seq 1 $max` + do + get $2 & + done +done diff --git a/wyatt.py b/wyatt.py new file mode 100644 index 0000000..d84db3e --- /dev/null +++ b/wyatt.py @@ -0,0 +1,84 @@ +import random +import sys + +# Initialize player attributes +player = { + "name": input("Enter your character's name: "), + "health": 100, + "food": 100, + "x": 5, + "y": 5, + "day": 1, +} + +# Define game resources +resources = { + "food": 50, + "water": 50, +} + +# Define game constants +MAP_WIDTH, MAP_HEIGHT = 20, 10 +PLAYER_CHAR = "(o)" +ENEMY_CHAR = "(?)" + +# Game loop +while player["health"] > 0: + # Create the game map + game_map = [[" " for _ in range(MAP_WIDTH)] for _ in range(MAP_HEIGHT)] + game_map[player["y"]][player["x"]] = PLAYER_CHAR + + # Place enemies randomly on the map + for _ in range(random.randint(1, 3)): + enemy_x = random.randint(0, MAP_WIDTH - 1) + enemy_y = random.randint(0, MAP_HEIGHT - 1) + game_map[enemy_y][enemy_x] = ENEMY_CHAR + + # Print the game map + for row in game_map: + print("".join(row)) + + print(f"\nDay {player['day']}") + print(f"Name: {player['name']}") + print(f"Health: {player['health']} HP {'*' * player['health']}") + print(f"Food: {player['food']} Hunger {'*' * player['food']}") + print(f"Coordinates: ({player['x']}, {player['y']})") + + # Player input for movement + move = input("Move (W/A/S/D): ").upper() + + # Update player position based on input + if move == "W" and player["y"] > 0: + player["y"] -= 1 + elif move == "S" and player["y"] < MAP_HEIGHT - 1: + player["y"] += 1 + elif move == "A" and player["x"] > 0: + player["x"] -= 1 + elif move == "D" and player["x"] < MAP_WIDTH - 1: + player["x"] += 1 + + # Consume resources + player["food"] -= random.randint(5, 15) + + # Check if the player has enough resources + if player["food"] < 0: + player["food"] = 0 + player["health"] -= 10 + + # Check if the player encounters an enemy + if game_map[player["y"]][player["x"]] == ENEMY_CHAR: + enemy_damage = random.randint(10, 30) + player["health"] -= enemy_damage + print(f"You encountered an enemy and took {enemy_damage} damage!") + + # Rest for the day + player["day"] += 1 + + # Exit the game if health reaches zero + if player["health"] <= 0: + print("Game Over. You did not survive.") + break + + input("Press Enter to continue to the next day...") + +sys.exit()