diff --git a/.gitignore b/.gitignore index c0c219b..69ad85d 100644 --- a/.gitignore +++ b/.gitignore @@ -55,7 +55,7 @@ coverage.xml *.pot # Django stuff: -*.log +#*.log local_settings.py db.sqlite3 db.sqlite3-journal @@ -122,3 +122,7 @@ dmypy.json # Pyre type checker .pyre/ + +docs/extras.html +docs/old_styles.css +src/app/logs/previous-runs/*.log \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index e654785..2ff857f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,5 +7,6 @@ "src/app/tests" ], "python.testing.unittestEnabled": false, - "python.testing.pytestEnabled": true + "python.testing.pytestEnabled": true, + "svg.preview.background": "transparent" } \ No newline at end of file diff --git a/README.md b/README.md index 3875fc0..3374dde 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,97 @@ # Musical Chairs -[![Deploy Jekyll](https://github.com/thomasthaddeus/musical-chairs/actions/workflows/jekyll-gh-pages.yml/badge.svg)](https://github.com/thomasthaddeus/musical-chairs/actions/workflows/jekyll-gh-pages.yml) +[![Deploy Jekyll](https://github.com/thomasthaddeus/musical-chairs/actions/workflows/jekyll-gh-pages.yml/badge.svg)](https://github.com/thomasthaddeus/musical-chairs/actions/workflows/jekyll-gh-pages.yml) ![GitHub top language](https://img.shields.io/github/languages/top/thomasthaddeus/musical-chairs?logo=python&logoColor=yellow) ![GitHub repo size](https://img.shields.io/github/repo-size/thomasthaddeus/musical-chairs?logo=github) -This project contains implementations of a seating arrangement algorithm using four different approaches: using a class, using Python's heapq module, using standalone functions, and using Python's zip function along with the built-in sorted function. +## Table of Contents + +
+ +- [Musical Chairs](#musical-chairs) + - [Table of Contents](#table-of-contents) + - [Introduction](#introduction) + - [The Musical Chairs Problem](#the-musical-chairs-problem) + - [The Algorithm](#the-algorithm) + - [The Web Application](#the-web-application) + - [About the Project](#about-the-project) + - [Built With](#built-with) + - [Directory Structure](#directory-structure) + - [Files Overview](#files-overview) + - [Running the Tests](#running-the-tests) + +
+ +## Introduction + +This project is a simple web application that demonstrates the use of four different approaches to solving the musical chairs problem. + +## The Musical Chairs Problem + +Suppose that you are hosting a dinner party and have invited some number of guests. When the guests arrive, you realize that you don't have enough chairs for everyone. You decide to solve this problem by asking some of the guests to stand while the others sit. Your guests are all very close friends and have no preference for where they sit at the table, but you would like to maximize the number of guests sitting at the table. + +## The Algorithm + +The algorithm is implemented in four different ways. The basic idea is to sort the guests by their total number of friends. Then, starting with the guest with the most friends, assign them a seat at the table and remove them and all of their friends from the list of guests. Repeat this process until there are no guests remaining. The different implementations of the algorithm differ in how the guests are sorted. + +## The Web Application + +The web application is built using [Jekyll](https://jekyllrb.com). The source code for the web application is located in the `src` directory. The web application is deployed to GitHub Pages and can be accessed via the following link [Musical Chairs](https://apparellnstuff.me/musical-chairs/) + +## About the Project + +This project contains implementations of a seating arrangement algorithm using four different approaches: + +1. using a class +2. using Python's `heapq` module +3. using standalone functions +4. using Python's `zip` function + - with the built-in `sorted` function. + +### Built With + +- [Python](https://www.python.org/) +- [pytest](https://docs.pytest.org/en/6.2.x/) +- [Jekyll](https://jekyllrb.com) + ## Directory Structure The project has the following directory structure: -```markdown +```bash . ├── .gitignore -├── LICENSE.md +├── LICENSE ├── README.md -├── src -│   ├── logs -│   │   ├── class.log -│   │   ├── hq.log -│   │   ├── standalone.log -│   │   └── zip.log -│   ├── _main -│   │   ├── _decorators_main.py -│   │   └── _functions_main.py -│   ├── main.py -│   ├── modules -│   │   ├── __init__.py -│   │   ├── with_classes.py -│   │   ├── with_heapq.py -│   │   ├── with_standalone.py -│   │   └── with_zip_sorted.py -│   └── tests -│   ├── __init__.py -│   ├── __pycache__ -│   ├── test_with_classes.py -│   ├── test_with_heapq.py -│   ├── test_with_standalone.py -│   └── test_with_zip_sorted.py -└── .vscode - └── settings.json +└── src + ├── app + │   ├── main.py + │   ├── pylint.rc + │   ├── logs + │   │   ├── class.log + │   │   ├── hq.log + │   │   ├── standalone.log + │   │   └── zip.log + │   ├── _main + │   │   ├── _decorators_main.py + │   │   └── _functions_main.py + │   ├── modules + │   │   ├── __init__.py + │   │   ├── with_classes.py + │   │   ├── with_heapq.py + │   │   ├── with_standalone.py + │   │   └── with_zip_sorted.py + │   └── tests + │   ├── __init__.py + │   ├── test_main.py + │   └── test_modules.py + ├── css + │ └── styles.css + └── js + ├── accordion.js + ├── dropdown_enhancements.js + ├── fetch.js + ├── myModal.js + └── smooth_scrolling.js ``` ### Files Overview @@ -48,14 +102,14 @@ The project has the following directory structure: - `with_heapq.py`: Implementation of the algorithm using Python's heapq module. - `with_standalone.py`: Implementation of the algorithm using standalone functions. - `with_zip_sorted.py`: Implementation of the algorithm using Python's zip function along with the built-in sorted function. -- `test_with_classes.py`, `test_with_heapq.py`, `test_with_standalone.py`, `test_with_zip_sorted.py`: These are pytest files for testing each of the four implementations. +- `test_main.py`, `test_modules.py`: These are pytest files for testing each of the four implementations. ### Running the Tests To run the tests, you will need to install pytest -```python -pip install pytest -``` + ```python + pip install pytest + ``` Then you can run pytest command from your terminal at the root directory of the project. diff --git a/assets/fonts/Source_Code_Pro/OFL.txt b/assets/fonts/Source_Code_Pro/OFL.txt new file mode 100644 index 0000000..366206f --- /dev/null +++ b/assets/fonts/Source_Code_Pro/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/assets/fonts/Source_Code_Pro/README.txt b/assets/fonts/Source_Code_Pro/README.txt new file mode 100644 index 0000000..2e57b72 --- /dev/null +++ b/assets/fonts/Source_Code_Pro/README.txt @@ -0,0 +1,79 @@ +Source Code Pro Variable Font +============================= + +This download contains Source Code Pro as both variable fonts and static fonts. + +Source Code Pro is a variable font with this axis: + wght + +This means all the styles are contained in these files: + SourceCodePro-VariableFont_wght.ttf + SourceCodePro-Italic-VariableFont_wght.ttf + +If your app fully supports variable fonts, you can now pick intermediate styles +that aren’t available as static fonts. Not all apps support variable fonts, and +in those cases you can use the static font files for Source Code Pro: + static/SourceCodePro-ExtraLight.ttf + static/SourceCodePro-Light.ttf + static/SourceCodePro-Regular.ttf + static/SourceCodePro-Medium.ttf + static/SourceCodePro-SemiBold.ttf + static/SourceCodePro-Bold.ttf + static/SourceCodePro-ExtraBold.ttf + static/SourceCodePro-Black.ttf + static/SourceCodePro-ExtraLightItalic.ttf + static/SourceCodePro-LightItalic.ttf + static/SourceCodePro-Italic.ttf + static/SourceCodePro-MediumItalic.ttf + static/SourceCodePro-SemiBoldItalic.ttf + static/SourceCodePro-BoldItalic.ttf + static/SourceCodePro-ExtraBoldItalic.ttf + static/SourceCodePro-BlackItalic.ttf + +Get started +----------- + +1. Install the font files you want to use + +2. Use your app's font picker to view the font family and all the +available styles + +Learn more about variable fonts +------------------------------- + + https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts + https://variablefonts.typenetwork.com + https://medium.com/variable-fonts + +In desktop apps + + https://theblog.adobe.com/can-variable-fonts-illustrator-cc + https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts + +Online + + https://developers.google.com/fonts/docs/getting_started + https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide + https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts + +Installing fonts + + MacOS: https://support.apple.com/en-us/HT201749 + Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux + Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows + +Android Apps + + https://developers.google.com/fonts/docs/android + https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts + +License +------- +Please read the full license text (OFL.txt) to understand the permissions, +restrictions and requirements for usage, redistribution, and modification. + +You can use them in your products & projects – print or digital, +commercial or otherwise. + +This isn't legal advice, please consider consulting a lawyer and see the full +license for all details. diff --git a/assets/fonts/Source_Code_Pro/SourceCodePro-Italic-VariableFont_wght.ttf b/assets/fonts/Source_Code_Pro/SourceCodePro-Italic-VariableFont_wght.ttf new file mode 100644 index 0000000..25febab Binary files /dev/null and b/assets/fonts/Source_Code_Pro/SourceCodePro-Italic-VariableFont_wght.ttf differ diff --git a/assets/fonts/Source_Code_Pro/SourceCodePro-VariableFont_wght.ttf b/assets/fonts/Source_Code_Pro/SourceCodePro-VariableFont_wght.ttf new file mode 100644 index 0000000..cd96a61 Binary files /dev/null and b/assets/fonts/Source_Code_Pro/SourceCodePro-VariableFont_wght.ttf differ diff --git a/assets/fonts/Source_Code_Pro/static/SourceCodePro-Black.ttf b/assets/fonts/Source_Code_Pro/static/SourceCodePro-Black.ttf new file mode 100644 index 0000000..cfc5f9f Binary files /dev/null and b/assets/fonts/Source_Code_Pro/static/SourceCodePro-Black.ttf differ diff --git a/assets/fonts/Source_Code_Pro/static/SourceCodePro-BlackItalic.ttf b/assets/fonts/Source_Code_Pro/static/SourceCodePro-BlackItalic.ttf new file mode 100644 index 0000000..b9e96b2 Binary files /dev/null and b/assets/fonts/Source_Code_Pro/static/SourceCodePro-BlackItalic.ttf differ diff --git a/assets/fonts/Source_Code_Pro/static/SourceCodePro-Bold.ttf b/assets/fonts/Source_Code_Pro/static/SourceCodePro-Bold.ttf new file mode 100644 index 0000000..e034f70 Binary files /dev/null and b/assets/fonts/Source_Code_Pro/static/SourceCodePro-Bold.ttf differ diff --git a/assets/fonts/Source_Code_Pro/static/SourceCodePro-BoldItalic.ttf b/assets/fonts/Source_Code_Pro/static/SourceCodePro-BoldItalic.ttf new file mode 100644 index 0000000..7b354cc Binary files /dev/null and b/assets/fonts/Source_Code_Pro/static/SourceCodePro-BoldItalic.ttf differ diff --git a/assets/fonts/Source_Code_Pro/static/SourceCodePro-ExtraBold.ttf b/assets/fonts/Source_Code_Pro/static/SourceCodePro-ExtraBold.ttf new file mode 100644 index 0000000..a66b0f4 Binary files /dev/null and b/assets/fonts/Source_Code_Pro/static/SourceCodePro-ExtraBold.ttf differ diff --git a/assets/fonts/Source_Code_Pro/static/SourceCodePro-ExtraBoldItalic.ttf b/assets/fonts/Source_Code_Pro/static/SourceCodePro-ExtraBoldItalic.ttf new file mode 100644 index 0000000..5041be4 Binary files /dev/null and b/assets/fonts/Source_Code_Pro/static/SourceCodePro-ExtraBoldItalic.ttf differ diff --git a/assets/fonts/Source_Code_Pro/static/SourceCodePro-ExtraLight.ttf b/assets/fonts/Source_Code_Pro/static/SourceCodePro-ExtraLight.ttf new file mode 100644 index 0000000..2b652af Binary files /dev/null and b/assets/fonts/Source_Code_Pro/static/SourceCodePro-ExtraLight.ttf differ diff --git a/assets/fonts/Source_Code_Pro/static/SourceCodePro-ExtraLightItalic.ttf b/assets/fonts/Source_Code_Pro/static/SourceCodePro-ExtraLightItalic.ttf new file mode 100644 index 0000000..0345c21 Binary files /dev/null and b/assets/fonts/Source_Code_Pro/static/SourceCodePro-ExtraLightItalic.ttf differ diff --git a/assets/fonts/Source_Code_Pro/static/SourceCodePro-Italic.ttf b/assets/fonts/Source_Code_Pro/static/SourceCodePro-Italic.ttf new file mode 100644 index 0000000..ffbae58 Binary files /dev/null and b/assets/fonts/Source_Code_Pro/static/SourceCodePro-Italic.ttf differ diff --git a/assets/fonts/Source_Code_Pro/static/SourceCodePro-Light.ttf b/assets/fonts/Source_Code_Pro/static/SourceCodePro-Light.ttf new file mode 100644 index 0000000..aae9d48 Binary files /dev/null and b/assets/fonts/Source_Code_Pro/static/SourceCodePro-Light.ttf differ diff --git a/assets/fonts/Source_Code_Pro/static/SourceCodePro-LightItalic.ttf b/assets/fonts/Source_Code_Pro/static/SourceCodePro-LightItalic.ttf new file mode 100644 index 0000000..c1f3d4b Binary files /dev/null and b/assets/fonts/Source_Code_Pro/static/SourceCodePro-LightItalic.ttf differ diff --git a/assets/fonts/Source_Code_Pro/static/SourceCodePro-Medium.ttf b/assets/fonts/Source_Code_Pro/static/SourceCodePro-Medium.ttf new file mode 100644 index 0000000..1e42b5e Binary files /dev/null and b/assets/fonts/Source_Code_Pro/static/SourceCodePro-Medium.ttf differ diff --git a/assets/fonts/Source_Code_Pro/static/SourceCodePro-MediumItalic.ttf b/assets/fonts/Source_Code_Pro/static/SourceCodePro-MediumItalic.ttf new file mode 100644 index 0000000..580c00d Binary files /dev/null and b/assets/fonts/Source_Code_Pro/static/SourceCodePro-MediumItalic.ttf differ diff --git a/assets/fonts/Source_Code_Pro/static/SourceCodePro-Regular.ttf b/assets/fonts/Source_Code_Pro/static/SourceCodePro-Regular.ttf new file mode 100644 index 0000000..daad874 Binary files /dev/null and b/assets/fonts/Source_Code_Pro/static/SourceCodePro-Regular.ttf differ diff --git a/assets/fonts/Source_Code_Pro/static/SourceCodePro-SemiBold.ttf b/assets/fonts/Source_Code_Pro/static/SourceCodePro-SemiBold.ttf new file mode 100644 index 0000000..7dc26d9 Binary files /dev/null and b/assets/fonts/Source_Code_Pro/static/SourceCodePro-SemiBold.ttf differ diff --git a/assets/fonts/Source_Code_Pro/static/SourceCodePro-SemiBoldItalic.ttf b/assets/fonts/Source_Code_Pro/static/SourceCodePro-SemiBoldItalic.ttf new file mode 100644 index 0000000..37595b0 Binary files /dev/null and b/assets/fonts/Source_Code_Pro/static/SourceCodePro-SemiBoldItalic.ttf differ diff --git a/assets/img/chair.svg b/assets/img/chair.svg new file mode 100644 index 0000000..15cb0cc --- /dev/null +++ b/assets/img/chair.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/_index.html b/docs/_index.html deleted file mode 100644 index 7b7aee5..0000000 --- a/docs/_index.html +++ /dev/null @@ -1,143 +0,0 @@ - - - - - - - - - - - - - - - - - - -
Header 1Header 2
Data 1Data 2
Data 3Data 4
- - - - - - - - - - - - - - -
- Hover over me - Tooltip text -
- - - - - - -
-

This is the content for Accordion 1.

-
- - -
-

This is the content for Accordion 2.

-
- - - - - - - - -
-

Welcome to Our Site

-

Discover amazing content and join our community.

-
- - - -
- -
- - - -
- Description -
Caption for the image
-
- - - - -
- -
- - - - -
- Hover over me - Tooltip text -
- - -
-

This is the content for Accordion 1.

-
- -
-

This is the content for Accordion 2.

-
- - -
- -
- -
- Description -
Caption for the image
-
- - -
- -
\ No newline at end of file diff --git a/docs/cloud_features.md b/docs/cloud_features.md index 3c76733..8b3cd18 100644 --- a/docs/cloud_features.md +++ b/docs/cloud_features.md @@ -1,5 +1,3 @@ -Implementing the features mentioned, especially with a cloud server, is a multi-step process. Here's a high-level overview of how you can achieve this: - ### 1. Setting Up a Cloud Server: **a. Choose a Cloud Provider**: @@ -113,4 +111,4 @@ gem install sorcery **d. Monitoring and Alerts**: Use tools like New Relic or Datadog to monitor your server and application performance. -This is a high-level overview, and each step has its intricacies. Depending on your exact requirements and the complexity of your application, you might need to adjust or add steps. If you'd like a deep dive into any specific step or need further assistance, please let me know! +This is a high-level overview, and each step has its intricacies. diff --git a/docs/index_bkup.html b/docs/index_bkup.html deleted file mode 100644 index b9ded2d..0000000 --- a/docs/index_bkup.html +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - Musical Chairs - - - - - -
-

Welcome to Musical Chairs

-
-
-
-

About

-

- This is a project by Thomasthaddeus. You can find more details in the - README. -

- -
-
-
-

Directory Structure

- - -
-
- - - - \ No newline at end of file diff --git a/index.html b/index.html index 742fb06..5e83eb7 100644 --- a/index.html +++ b/index.html @@ -1,76 +1,115 @@ + - - - Musical Chairs - + + + Musical Chairs + + -
-

Welcome to Musical Chairs

-
-
-
-
-

About

-

This is a project by Thomasthaddeus. You can find more details in the README.

+
+

Welcome to Musical Chairs

+
+
+
+

The Algorithm

+

The algorithm is implemented in four different ways. The basic idea is to sort the guests by their total number + of friends. Then, starting with the guest with the most friends, assign them a seat at the table and remove them + and all of their friends from the list of guests. Repeat this process until there are no guests remaining. The + different implementations of the algorithm differ in how the guests are sorted.

+

Using a Class

+

The first implementation uses a class to represent the guests. The class has two attributes: the total number + of friends and a list of the guest's friends. The class also has a method to calculate the total number of + friends and a method to remove a guest and all of their friends from a list of guests.

+

Using Python's heapq Module

+

The second implementation uses Python's heapq module to sort the guests by their total number of friends. The + heapq module is a priority queue implementation. It is used here to sort the guests by their total number of + friends in descending order. This approach requires that the guests be represented as a list of tuples + containing the total number of friends and the list of friends.

+

Using Standalone Functions

+ The third implementation uses standalone functions to sort the guests by their total number of friends. The first + function calculates the total number of friends for a given guest. The second function sorts the guests by their + total number of friends in descending order. This approach requires that the guests be represented as a list of + tuples containing the total number of friends and the list of friends. +

Using Python's zip Function

+

The fourth implementation uses Python's zip function along with the built-in sorted function to sort the guests + by their total number of friends. The zip function takes two lists and returns a list of tuples containing the + corresponding elements of each list. The sorted function takes a list of tuples and sorts them by the first + element of each tuple. This approach requires that the guests be represented as two lists: one containing the + total number of friends and the other containing the list of friends. +

+ +
+

About

+

+ This is a project by Thomasthaddeus. You can find more details in + the + README. +

+ +
+
+
+
+

Directory Structure

+
-
-

Directory Structure

- +
-
- - -
-
- - - - - - +
+ + + +
+
+ + +
+
+ + + + + + - + + \ No newline at end of file diff --git a/pylint.toml b/pylint.toml new file mode 100644 index 0000000..3965c12 --- /dev/null +++ b/pylint.toml @@ -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 = true + +# 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.0 + +# 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.11" + +# 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"] + +# 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 = ["c-extension-no-member"] + +[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, json +# 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. Available dictionaries: en (aspell), en_AU (aspell), +# en_CA (aspell), en_GB (aspell), en_US (aspell). +# 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"] diff --git a/src/app/_main/_decorators_main.py b/src/app/_main/_decorators_main.py index cf10c9b..344953a 100644 --- a/src/app/_main/_decorators_main.py +++ b/src/app/_main/_decorators_main.py @@ -1,5 +1,4 @@ -""" -_decorators_main.py +"""_decorators_main.py This module provides four implementations of a seating arrangement algorithm and uses decorators to measure the execution time and handle logging. diff --git a/src/app/_main/_functions_main.py b/src/app/_main/_functions_main.py index 598e2d0..ae4e782 100644 --- a/src/app/_main/_functions_main.py +++ b/src/app/_main/_functions_main.py @@ -44,7 +44,9 @@ def setup_logger(name) -> tuple[Logger, FileHandler]: return logger, handler def run_and_log(logger, function, *args, **kwargs): - """Run a function, log its output and execution time, and return its output.""" + """ + Run a function, log its output and execution time, and return its output. + """ start: float = timeit.default_timer() output = function(*args, **kwargs) end: float = timeit.default_timer() @@ -58,13 +60,19 @@ def cleanup_logger(logger, handler) -> None: handler.close() def test_run(name, function, *args, **kwargs) -> None: - """Perform a single test run of a function, including setup, execution, logging, and cleanup.""" + """ + Perform a single test run of a function, including setup, execution, + logging, and cleanup. + """ logger, handler = setup_logger(name) run_and_log(logger, function, *args, **kwargs) cleanup_logger(logger, handler) def main() -> None: - """Execute and compare the outputs of four implementations of a seating arrangement algorithm.""" + """ + Execute and compare the outputs of four implementations of a seating + arrangement algorithm. + """ test_runs = 5 pause_time = 1 diff --git a/src/app/_main/_main-old.py b/src/app/_main/_main-old.py new file mode 100644 index 0000000..17e471e --- /dev/null +++ b/src/app/_main/_main-old.py @@ -0,0 +1,121 @@ +"""main.py +This module provides four implementations of a seating arrangement algorithm. + +The algorithm is used to find a possible arrangement of people entering a hall +according to the following rules: +- Boys choose a row where both seats are free, and out of those rows, they pick + the smallest one. +- Girls choose a row where one seat is taken by a boy, and out of those rows, + they pick the largest one. + +The implementations use different strategies: +1. Using a class: SeatingArrangement +2. Using heapq: hq_seat_arrange +3. Using standalone functions: sort_seats, arrange_seats +4. Using zip and sorted: zip_seat_arrange +""" + +import time +import timeit +import logging +from modules.with_classes import SeatingArrangement +from modules.with_heapq import hq_seat_arrange +from modules.with_standalone import sort_seats, arrange_seats +from modules.with_zip_sorted import zip_seat_arrange + +# Constants +N = 2 # number of rows +S = "0011" # sequence +W = [2, 1] # width + + +def main() -> None: + """ + Executes and compares the outputs of four implementations of a seating + arrangement algorithm. + + The implementations are executed with the same input, their outputs and + their execution times are logged into separate files. + """ + + # adjust this to change the number of times each test is run + test_runs = 5 + # adjust this to change the length of the pause between tests (in seconds) + pause_time = 1 + + for i in range(test_runs): + # set up a separate logger for each method + class_logger = logging.getLogger("class") + class_logger.setLevel(logging.INFO) + hq_logger = logging.getLogger("hq") + hq_logger.setLevel(logging.INFO) + standalone_logger = logging.getLogger("standalone") + standalone_logger.setLevel(logging.INFO) + zip_logger = logging.getLogger("zip") + zip_logger.setLevel(logging.INFO) + + # set up a file handler for each logger + class_handler = logging.FileHandler("src/app/logs/class.log") + hq_handler = logging.FileHandler("src/app/logs/hq.log") + standalone_handler = logging.FileHandler("src/app/logs/standalone.log") + zip_handler = logging.FileHandler("src/app/logs/zip.log") + + # add the handlers to the loggers + class_logger.addHandler(class_handler) + hq_logger.addHandler(hq_handler) + standalone_logger.addHandler(standalone_handler) + zip_logger.addHandler(zip_handler) + + # Using the class: + class_logger.info("\nTest run %s:", i+1) + start: float = timeit.default_timer() + arrangement = SeatingArrangement(N, S, W) + class_logger.info(arrangement.arrange_seats()) + end: float = timeit.default_timer() + class_logger.info("Execution time: %s", end - start) + + time.sleep(pause_time) + + # Using heapq: + hq_logger.info("\nTest run %s:", i+1) + start = timeit.default_timer() + hq_logger.info(hq_seat_arrange(N, S, W)) + end = timeit.default_timer() + hq_logger.info("Execution time: %s", end - start) + + time.sleep(pause_time) + + # Using standalone functions: + standalone_logger.info("\nTest run %s:", i+1) + start = timeit.default_timer() + seats = sort_seats(W) + standalone_logger.info(arrange_seats(N, S, seats)) + end = timeit.default_timer() + standalone_logger.info("Execution time: %s", end - start) + + time.sleep(pause_time) + + # Using zip and sorted: + zip_logger.info("\nTest run %s:", i+1) + start = timeit.default_timer() + zip_logger.info(zip_seat_arrange(N, S, W)) + end = timeit.default_timer() + zip_logger.info("Execution time: %s", end - start) + + time.sleep(pause_time) + + # remove the handlers at the end of each test run + class_logger.removeHandler(class_handler) + hq_logger.removeHandler(hq_handler) + standalone_logger.removeHandler(standalone_handler) + zip_logger.removeHandler(zip_handler) + + # close the handlers at the end of each test run + class_handler.close() + hq_handler.close() + standalone_handler.close() + zip_handler.close() + + +if __name__ == "__main__": + main() diff --git a/src/app/logs/class.log b/src/app/logs/class.log new file mode 100644 index 0000000..d128060 --- /dev/null +++ b/src/app/logs/class.log @@ -0,0 +1,20 @@ + +Test run 1: +[[1, True, True], [2, True, True]] +Execution time: 1.279998105019331e-05 + +Test run 2: +[[1, True, True], [2, True, True]] +Execution time: 8.600007276982069e-06 + +Test run 3: +[[1, True, True], [2, True, True]] +Execution time: 8.600007276982069e-06 + +Test run 4: +[[1, True, True], [2, True, True]] +Execution time: 1.0599964298307896e-05 + +Test run 5: +[[1, True, True], [2, True, True]] +Execution time: 9.09995287656784e-06 diff --git a/src/app/logs/combined.log b/src/app/logs/combined.log new file mode 100644 index 0000000..8b75831 --- /dev/null +++ b/src/app/logs/combined.log @@ -0,0 +1,88 @@ +# -------------- Standalone test run -------------- + +Test run 1: +[[1, True, True], [2, True, True]] +Execution time: 2.8800044674426317e-05 + +Test run 2: +[[1, True, True], [2, True, True]] +Execution time: 5.199981387704611e-06 + +Test run 3: +[[1, True, True], [2, True, True]] +Execution time: 5.89998671784997e-06 + +Test run 4: +[[1, True, True], [2, True, True]] +Execution time: 5.599984433501959e-06 + +Test run 5: +[[1, True, True], [2, True, True]] +Execution time: 5.799985956400633e-06 + + +# -------------- Class test run -------------- + +Test run 1: +[[1, True, True], [2, True, True]] +Execution time: 1.279998105019331e-05 + +Test run 2: +[[1, True, True], [2, True, True]] +Execution time: 8.600007276982069e-06 + +Test run 3: +[[1, True, True], [2, True, True]] +Execution time: 8.600007276982069e-06 + +Test run 4: +[[1, True, True], [2, True, True]] +Execution time: 1.0599964298307896e-05 + +Test run 5: +[[1, True, True], [2, True, True]] +Execution time: 9.09995287656784e-06 + +# -------------- heapq test run -------------- + +Test run 1: +['BG', 'BG'] +Execution time: 5.089998012408614e-05 + +Test run 2: +['BG', 'BG'] +Execution time: 5.070003680884838e-05 + +Test run 3: +['BG', 'BG'] +Execution time: 5.770003190264106e-05 + +Test run 4: +['BG', 'BG'] +Execution time: 1.5400000847876072e-05 + +Test run 5: +['BG', 'BG'] +Execution time: 1.8099963199347258e-05 + +# -------------- zip test run -------------- + +Test run 1: +['B', 'B'] +Execution time: 3.549997927621007e-05 + +Test run 2: +['B', 'B'] +Execution time: 4.449998959898949e-05 + +Test run 3: +['B', 'B'] +Execution time: 2.3100001271814108e-05 + +Test run 4: +['B', 'B'] +Execution time: 3.809999907389283e-05 + +Test run 5: +['B', 'B'] +Execution time: 4.140002420172095e-05 diff --git a/src/app/logs/hq.log b/src/app/logs/hq.log new file mode 100644 index 0000000..e19805a --- /dev/null +++ b/src/app/logs/hq.log @@ -0,0 +1,20 @@ + +Test run 1: +['BG', 'BG'] +Execution time: 5.089998012408614e-05 + +Test run 2: +['BG', 'BG'] +Execution time: 5.070003680884838e-05 + +Test run 3: +['BG', 'BG'] +Execution time: 5.770003190264106e-05 + +Test run 4: +['BG', 'BG'] +Execution time: 1.5400000847876072e-05 + +Test run 5: +['BG', 'BG'] +Execution time: 1.8099963199347258e-05 diff --git a/src/app/logs/standalone.log b/src/app/logs/standalone.log new file mode 100644 index 0000000..7149fdc --- /dev/null +++ b/src/app/logs/standalone.log @@ -0,0 +1,20 @@ + +Test run 1: +[[1, True, True], [2, True, True]] +Execution time: 2.8800044674426317e-05 + +Test run 2: +[[1, True, True], [2, True, True]] +Execution time: 5.199981387704611e-06 + +Test run 3: +[[1, True, True], [2, True, True]] +Execution time: 5.89998671784997e-06 + +Test run 4: +[[1, True, True], [2, True, True]] +Execution time: 5.599984433501959e-06 + +Test run 5: +[[1, True, True], [2, True, True]] +Execution time: 5.799985956400633e-06 diff --git a/src/app/logs/zip.log b/src/app/logs/zip.log new file mode 100644 index 0000000..d7863bf --- /dev/null +++ b/src/app/logs/zip.log @@ -0,0 +1,20 @@ + +Test run 1: +['B', 'B'] +Execution time: 3.549997927621007e-05 + +Test run 2: +['B', 'B'] +Execution time: 4.449998959898949e-05 + +Test run 3: +['B', 'B'] +Execution time: 2.3100001271814108e-05 + +Test run 4: +['B', 'B'] +Execution time: 3.809999907389283e-05 + +Test run 5: +['B', 'B'] +Execution time: 4.140002420172095e-05 diff --git a/src/app/main.py b/src/app/main.py index e402412..07226bd 100644 --- a/src/app/main.py +++ b/src/app/main.py @@ -1,12 +1,11 @@ """main.py -This module provides four implementations of a seating arrangement algorithm. +This module benchmarks four implementations of a seating arrangement algorithm. -The algorithm is used to find a possible arrangement of people entering a hall -according to the following rules: -- Boys choose a row where both seats are free, and out of those rows, they pick - the smallest one. -- Girls choose a row where one seat is taken by a boy, and out of those rows, - they pick the largest one. +The algorithm determines the seating arrangement of people entering a hall +based on: + - Boys choosing a row where both seats are free, selecting the smallest row. + - Girls choosing a row where one seat is occupied by a boy, selecting the + largest row. The implementations use different strategies: 1. Using a class: SeatingArrangement @@ -15,7 +14,6 @@ 4. Using zip and sorted: zip_seat_arrange """ -import os import time import timeit import logging @@ -25,97 +23,99 @@ from modules.with_zip_sorted import zip_seat_arrange # Constants -N = 2 # number of rows -S = "0011" # sequence -W = [2, 1] # width +N = 2 # number of rows +S = "0011" # sequence +W = [2, 1] # width of rows -def main() -> None: +def run_and_log(method, logger, *args): """ - Executes and compares the outputs of four implementations of a seating - arrangement algorithm. + Execute a method, log its output and execution time. - The implementations are executed with the same input, their outputs and - their execution times are logged into separate files. + Args: + method (callable): The method to be executed. + logger (logging.Logger): Logger to log the results and execution time. + *args: Variable length argument list for the method. """ + try: + start = timeit.default_timer() + result = method(*args) + end = timeit.default_timer() + logger.info(result) + logger.info("Execution time: %s", end - start) + except (IndexError, TypeError, ValueError) as err: + logger.error(f"Error executing method: {err}") - # adjust this to change the number of times each test is run - test_runs = 5 - # adjust this to change the length of the pause between tests (in seconds) - pause_time = 1 +def benchmark_methods(test_runs, pause_time, loggers): + """ + Benchmark the seating arrangement methods and log their outputs and execution times. + + Args: + test_runs (int): Number of times each method is executed. + pause_time (float): Time in seconds to pause between each method execution. + loggers (dict): Dictionary of loggers for each method. + """ for i in range(test_runs): - # set up a separate logger for each method - class_logger = logging.getLogger("class") - class_logger.setLevel(logging.INFO) - hq_logger = logging.getLogger("hq") - hq_logger.setLevel(logging.INFO) - standalone_logger = logging.getLogger("standalone") - standalone_logger.setLevel(logging.INFO) - zip_logger = logging.getLogger("zip") - zip_logger.setLevel(logging.INFO) - - # set up a file handler for each logger - class_handler = logging.FileHandler("src/logs/class.log") - hq_handler = logging.FileHandler("src/logs/hq.log") - standalone_handler = logging.FileHandler("src/logs/standalone.log") - zip_handler = logging.FileHandler("src/logs/zip.log") - - # add the handlers to the loggers - class_logger.addHandler(class_handler) - hq_logger.addHandler(hq_handler) - standalone_logger.addHandler(standalone_handler) - zip_logger.addHandler(zip_handler) + for _, logger in loggers.items(): + logger.info("\nTest run %s:", i + 1) # Using the class: - class_logger.info("\nTest run %s:", i+1) - start: float = timeit.default_timer() - arrangement = SeatingArrangement(N, S, W) - class_logger.info(arrangement.arrange_seats()) - end: float = timeit.default_timer() - class_logger.info("Execution time: %s", end - start) - + run_and_log(lambda: SeatingArrangement(N, S, W).arrange_seats(), loggers["class"]) time.sleep(pause_time) # Using heapq: - hq_logger.info("\nTest run %s:", i+1) - start = timeit.default_timer() - hq_logger.info(hq_seat_arrange(N, S, W)) - end = timeit.default_timer() - hq_logger.info("Execution time: %s", end - start) - + run_and_log(hq_seat_arrange, loggers["hq"], N, S, W) time.sleep(pause_time) # Using standalone functions: - standalone_logger.info("\nTest run %s:", i+1) - start = timeit.default_timer() seats = sort_seats(W) - standalone_logger.info(arrange_seats(N, S, seats)) - end = timeit.default_timer() - standalone_logger.info("Execution time: %s", end - start) - + run_and_log(arrange_seats, loggers["standalone"], N, S, seats) time.sleep(pause_time) # Using zip and sorted: - zip_logger.info("\nTest run %s:", i+1) - start = timeit.default_timer() - zip_logger.info(zip_seat_arrange(N, S, W)) - end = timeit.default_timer() - zip_logger.info("Execution time: %s", end - start) - + run_and_log(zip_seat_arrange, loggers["zip"], N, S, W) time.sleep(pause_time) - # remove the handlers at the end of each test run - class_logger.removeHandler(class_handler) - hq_logger.removeHandler(hq_handler) - standalone_logger.removeHandler(standalone_handler) - zip_logger.removeHandler(zip_handler) - - # close the handlers at the end of each test run - class_handler.close() - hq_handler.close() - standalone_handler.close() - zip_handler.close() + +def main() -> None: + """ + Executes and compares the outputs of four implementations of a seating + arrangement algorithm. + + The implementations are executed with the same input, and their outputs and + execution times are logged into separate files. + """ + # adjust this to change the number of times each test is run + test_runs = 5 + # adjust this to change the length of the pause between tests (in seconds) + pause_time = 1 + + # set up a separate logger for each method + loggers = { + "class": logging.getLogger("class"), + "hq": logging.getLogger("hq"), + "standalone": logging.getLogger("standalone"), + "zip": logging.getLogger("zip") + } + + handlers = { + "class": logging.FileHandler("src/app/logs/class.log"), + "hq": logging.FileHandler("src/app/logs/hq.log"), + "standalone": logging.FileHandler("src/app/logs/standalone.log"), + "zip": logging.FileHandler("src/app/logs/zip.log") + } + + for name, logger in loggers.items(): + logger.setLevel(logging.INFO) + logger.addHandler(handlers[name]) + + # Run the benchmark + benchmark_methods(test_runs, pause_time, loggers) + + # close the handlers at the end of the script + for handler in handlers.values(): + handler.close() if __name__ == "__main__": diff --git a/src/app/modules/with_classes.py b/src/app/modules/with_classes.py index 1d5663d..8fc4c55 100644 --- a/src/app/modules/with_classes.py +++ b/src/app/modules/with_classes.py @@ -16,7 +16,7 @@ """ -class SeatingArrangement: +class SeatingArrangement: # pylint: disable=too-few-public-methods """A class to represent a seating arrangement in a hall. Attributes: diff --git a/src/app/modules/with_heapq.py b/src/app/modules/with_heapq.py index 094cdb1..39aa0f3 100644 --- a/src/app/modules/with_heapq.py +++ b/src/app/modules/with_heapq.py @@ -23,7 +23,7 @@ import heapq -def hq_seat_arrange(N, S, W): +def hq_seat_arrange(N, S, W): # pylint: disable=invalid-name """Finds the optimal seating arrangement in a hall using a heap-based algorithm. Args: @@ -53,9 +53,13 @@ def hq_seat_arrange(N, S, W): if person == '0': # Boy # Get the row with minimum width from boys_heap _, row = heapq.heappop(boys_heap) - seats[row] = 'B' # Mark this row as occupied by a boy + + # Mark this row as occupied by a boy + seats[row] = 'B' + # Push this row into girls_heap - heapq.heappush(girls_heap, [-W[row], row]) # We use -W[row] because Python's heapq provides min heap + # We use -W[row] because Python's heapq provides min heap + heapq.heappush(girls_heap, [-W[row], row]) else: # Girl # Get the row with maximum width from girls_heap _, row = heapq.heappop(girls_heap) diff --git a/src/app/tests/test_main.py b/src/app/tests/test_main.py new file mode 100644 index 0000000..4c8a6a0 --- /dev/null +++ b/src/app/tests/test_main.py @@ -0,0 +1,162 @@ +""" +_summary_ + +1. **Basic Functionality Tests**: + - Test each method with a variety of inputs to ensure they produce the + expected outputs. + - Test with different numbers of rows, sequences, and widths. + +2. **Edge Cases**: + - Test with an empty hall (no rows). + - Test with a sequence containing only boys or only girls. + - Test with all rows having the same width. + - Test with a sequence longer than the number of available seats. + - Test with a sequence shorter than the number of available seats. + +3. **Invalid Inputs**: + - Test with negative numbers of rows. + - Test with non-binary sequences (i.e., sequences containing characters + other than '0' and '1'). + - Test with mismatched lengths of sequence and width lists. + - Test with negative or zero widths. + +4. **Performance Tests**: + - Test with a large number of rows and a long sequence to measure the + performance of each method. + - Compare the execution times of the different methods to determine which + is the most efficient. + +5. **Consistency Tests**: + - Ensure that all methods produce the same output for the same input. + - This can be done by comparing the outputs of the different methods for a + variety of inputs. + +6. **Logging Tests**: + - Ensure that the logging mechanism works correctly. + - Check that logs are being written to the correct files. + - Check that the logs contain the expected information (e.g., results and + execution times). + +7. **Error Handling**: + - Ensure that the methods handle errors gracefully and log appropriate + error messages. + - For instance, when testing invalid inputs, check that the methods don't + crash and instead log an error. + +8. **Concurrency Tests** (if applicable in the future): + - If there's a possibility of these methods being called concurrently in + the future, test how they behave under concurrent access. This is more + advanced and might not be necessary given the current scope, but it's + something to consider for more complex applications. +""" + +import pytest +from modules.with_classes import SeatingArrangement +from modules.with_heapq import hq_seat_arrange +from modules.with_standalone import sort_seats, arrange_seats +from modules.with_zip_sorted import zip_seat_arrange + +# List of all the methods for easy testing +methods = [ + lambda N, S, W: SeatingArrangement(N, S, W).arrange_seats(), + hq_seat_arrange, + lambda N, S, W: arrange_seats(N, S, sort_seats(W)), + zip_seat_arrange +] + +#------------------- 1. Basic Functionality Tests ------------------------------ +@pytest.mark.parametrize("method", methods) +def test_basic_functionality(method): + N = 2 + S = '0011' + W = [2, 1] + # Expected output can be adjusted based on the actual expected results + expected_output = ... # Replace with the expected output + assert method(N, S, W) == expected_output + +#------------------------- 2. Edge Cases ---------------------------- +@pytest.mark.parametrize("method", methods) +def test_empty_hall(method): + N = 0 + S = '' + W = [] + assert method(N, S, W) == [] + +@pytest.mark.parametrize("method", methods) +def test_only_boys(method): + N = 2 + S = '00' + W = [2, 1] + # Adjust expected_output accordingly + expected_output = ... + assert method(N, S, W) == expected_output + +@pytest.mark.parametrize("method", methods) +def test_only_girls(method): + N = 2 + S = '11' + W = [2, 1] + # Adjust expected_output accordingly + expected_output = ... + assert method(N, S, W) == expected_output + +@pytest.mark.parametrize("method", methods) +def test_same_width(method): + N = 2 + S = '0011' + W = [2, 2] + # Adjust expected_output accordingly + expected_output = ... + assert method(N, S, W) == expected_output + +# ------------------------ 3. Invalid Inputs ---------------------------------- +@pytest.mark.parametrize("method", methods) +def test_negative_rows(method): + N = -2 + S = '0011' + W = [2, 1] + with pytest.raises(ValueError): + method(N, S, W) + +@pytest.mark.parametrize("method", methods) +def test_non_binary_sequence(method): + N = 2 + S = '0021' + W = [2, 1] + with pytest.raises(ValueError): + method(N, S, W) + +@pytest.mark.parametrize("method", methods) +def test_mismatched_lengths(method): + N = 2 + S = '0011' + W = [2] + with pytest.raises(ValueError): + method(N, S, W) + +@pytest.mark.parametrize("method", methods) +def test_negative_width(method): + N = 2 + S = '0011' + W = [-2, 1] + with pytest.raises(ValueError): + method(N, S, W) + +# ---------------------------- 4. Performance Tests ------------------------ +# This is more for benchmarking and might not have a clear 'expected' result. +# It's more about ensuring the methods can handle large inputs without errors. +@pytest.mark.parametrize("method", methods) +def test_large_input(method): + N = 1000 + S = '0' * 500 + '1' * 500 + W = [i for i in range(1, 1001)] + # No assertion here, just checking for successful execution + method(N, S, W) + +# ------------------------- 5. Consistency Tests ----------------------------- +def test_consistency(): + N = 2 + S = '0011' + W = [2, 1] + results = [method(N, S, W) for method in methods] + assert all(result == results[0] for result in results) diff --git a/src/app/tests/test_modules.py b/src/app/tests/test_modules.py new file mode 100644 index 0000000..2882d4f --- /dev/null +++ b/src/app/tests/test_modules.py @@ -0,0 +1,77 @@ +"""test_modules.py + +This module contains unit tests for various seating arrangement functions and +classes. The tests ensure that given a seating configuration and weights, the +seating arrangement methods return the expected results. + +The module imports several seating arrangement methods from different modules: +- SeatingArrangement class from 'with_classes' +- sort_seats and arrange_seats functions from 'with_standalone' +- hq_seat_arrange function from 'with_heapq' +- zip_seat_arrange function from 'with_zip_sorted' + +Each test function in this module corresponds to one of the imported seating +arrangement methods. The tests are designed to verify the correctness of the +seating arrangement logic implemented in each method. + +Note: + It's recommended to run these tests after making changes to any of the + seating arrangement methods to ensure their continued correctness. +""" + +import pytest +from with_standalone import sort_seats, arrange_seats +from with_heapq import hq_seat_arrange +from with_zip_sorted import zip_seat_arrange +from with_classes import SeatingArrangement + + +def test_seating_arrangement_class(): + """ + Test the SeatingArrangement class for correct seat arrangement. + + Given a seating configuration and weights, this test ensures that + the SeatingArrangement class correctly arranges the seats. + """ + N = 2 + S = '0011' + W = [2, 1] + arrangement = SeatingArrangement(N, S, W) + assert arrangement.arrange_seats() == [[1, True, True], [2, True, True]] + +def test_zip_seat_arrange(): + """ + Test the zip_seat_arrange function for correct seat arrangement. + + Given a seating configuration and weights, this test ensures that + the zip_seat_arrange function correctly arranges the seats. + """ + N = 2 + S = '0011' + W = [2, 1] + assert zip_seat_arrange(N, S, W) == ['BG', 'BG'] + +def test_seating_arrangement_functions(): + """ + Test the standalone seating arrangement functions for correct seat arrangement. + + Given a seating configuration and weights, this test ensures that + the standalone functions correctly arrange the seats. + """ + N = 2 + S = '0011' + W = [2, 1] + seats = sort_seats(W) + assert arrange_seats(N, S, seats) == [[1, True, True], [2, True, True]] + +def test_hq_seat_arrange(): + """ + Test the hq_seat_arrange function for correct seat arrangement. + + Given a seating configuration and weights, this test ensures that + the hq_seat_arrange function correctly arranges the seats. + """ + N = 2 + S = '0011' + W = [2, 1] + assert hq_seat_arrange(N, S, W) == ['BG', 'BG'] diff --git a/src/app/tests/test_with_classes.py b/src/app/tests/test_with_classes.py deleted file mode 100644 index 234b6eb..0000000 --- a/src/app/tests/test_with_classes.py +++ /dev/null @@ -1,9 +0,0 @@ -import pytest -from with_classes import SeatingArrangement # Assuming your class is defined in this module - -def test_seating_arrangement_class(): - N = 2 - S = '0011' - W = [2, 1] - arrangement = SeatingArrangement(N, S, W) - assert arrangement.arrange_seats() == [[1, True, True], [2, True, True]] diff --git a/src/app/tests/test_with_heapq.py b/src/app/tests/test_with_heapq.py deleted file mode 100644 index 21661eb..0000000 --- a/src/app/tests/test_with_heapq.py +++ /dev/null @@ -1,8 +0,0 @@ -import pytest -from with_heapq import hq_seat_arrange # Assuming your function is defined in this module - -def test_hq_seat_arrange(): - N = 2 - S = '0011' - W = [2, 1] - assert hq_seat_arrange(N, S, W) == ['BG', 'BG'] diff --git a/src/app/tests/test_with_standalone.py b/src/app/tests/test_with_standalone.py deleted file mode 100644 index fb1f4ea..0000000 --- a/src/app/tests/test_with_standalone.py +++ /dev/null @@ -1,9 +0,0 @@ -import pytest -from with_standalone import sort_seats, arrange_seats # Assuming your functions are defined in this module - -def test_seating_arrangement_functions(): - N = 2 - S = '0011' - W = [2, 1] - seats = sort_seats(W) - assert arrange_seats(N, S, seats) == [[1, True, True], [2, True, True]] diff --git a/src/app/tests/test_with_zip_sorted.py b/src/app/tests/test_with_zip_sorted.py deleted file mode 100644 index f816854..0000000 --- a/src/app/tests/test_with_zip_sorted.py +++ /dev/null @@ -1,8 +0,0 @@ -import pytest -from seating_arrangement_zip import seating_arrangement # Assuming your function is defined in this module - -def test_seating_arrangement_zip(): - N = 2 - S = '0011' - W = [2, 1] - assert seating_arrangement(N, S, W) == ['BG', 'BG'] diff --git a/src/css/old_styles.css b/src/css/old_styles.css deleted file mode 100644 index d1fd563..0000000 --- a/src/css/old_styles.css +++ /dev/null @@ -1,99 +0,0 @@ -/* Base Styles */ -body { - font-family: 'Arial', sans-serif; - line-height: 1.6; - background-color: #f4f4f4; - color: #333; - margin: 0; - padding: 0; -} - -a { - color: #007BFF; - text-decoration: none; - transition: color 0.3s; -} - -a:hover { - color: #0056b3; -} - -h1, h2, h3, h4, h5, h6 { - margin-top: 0; -} - -/* Header Styles */ -header { - background-color: #333; - color: #fff; - text-align: center; - padding: 1rem 0; -} - -/* Main Content Styles */ -main { - padding: 20px; - max-width: 1200px; - margin: 20px auto; - background-color: #fff; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); -} - -section { - margin-bottom: 20px; -} - -/* Dropdown Menu Styles */ -.dropdown { - position: relative; - display: inline-block; - margin-right: 20px; -} - -.dropdown-content { - display: none; - position: absolute; - background-color: #f9f9f9; - min-width: 160px; - box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); - z-index: 1; - border: 1px solid #ddd; -} - -.dropdown-content a { - color: black; - padding: 12px 16px; - text-decoration: none; - display: block; -} - -.dropdown-content a:hover { - background-color: #f1f1f1; -} - -.dropdown:hover .dropdown-content { - display: block; -} - -/* Footer Styles */ -footer { - background-color: #333; - color: #fff; - text-align: center; - padding: 1rem 0; - margin-top: 20px; -} - - -footer a { - color: #fff; - padding: 5px 10px; - border: 1px solid #fff; - border-radius: 5px; - transition: background-color 0.3s, color 0.3s; -} - -footer a:hover { - background-color: #fff; - color: #333; -} \ No newline at end of file