diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..ca96f23 Binary files /dev/null and b/.DS_Store differ diff --git a/.eggs/README.txt b/.eggs/README.txt new file mode 100644 index 0000000..5d01668 --- /dev/null +++ b/.eggs/README.txt @@ -0,0 +1,6 @@ +This directory contains eggs that were downloaded by setuptools to build, test, and run plug-ins. + +This directory caches those eggs to prevent repeated downloads. + +However, it is safe to delete this directory. + diff --git a/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/LICENSE b/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/LICENSE new file mode 100644 index 0000000..353924b --- /dev/null +++ b/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/LICENSE @@ -0,0 +1,19 @@ +Copyright Jason R. Coombs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/PKG-INFO b/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/PKG-INFO new file mode 100644 index 0000000..75c36d6 --- /dev/null +++ b/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/PKG-INFO @@ -0,0 +1,185 @@ +Metadata-Version: 2.1 +Name: pytest-runner +Version: 6.0.1 +Summary: Invoke py.test as distutils command with dependency resolution +Home-page: https://github.com/pytest-dev/pytest-runner/ +Author: Jason R. Coombs +Author-email: jaraco@jaraco.com +Classifier: Development Status :: 7 - Inactive +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Framework :: Pytest +Requires-Python: >=3.7 +License-File: LICENSE +Provides-Extra: docs +Requires-Dist: sphinx ; extra == 'docs' +Requires-Dist: jaraco.packaging >=9 ; extra == 'docs' +Requires-Dist: rst.linker >=1.9 ; extra == 'docs' +Requires-Dist: jaraco.tidelift >=1.4 ; extra == 'docs' +Provides-Extra: testing +Requires-Dist: pytest >=6 ; extra == 'testing' +Requires-Dist: pytest-checkdocs >=2.4 ; extra == 'testing' +Requires-Dist: pytest-flake8 ; extra == 'testing' +Requires-Dist: pytest-cov ; extra == 'testing' +Requires-Dist: pytest-enabler >=1.0.1 ; extra == 'testing' +Requires-Dist: pytest-virtualenv ; extra == 'testing' +Requires-Dist: types-setuptools ; extra == 'testing' +Requires-Dist: pytest-black >=0.3.7 ; (platform_python_implementation != "PyPy") and extra == 'testing' +Requires-Dist: pytest-mypy >=0.9.1 ; (platform_python_implementation != "PyPy") and extra == 'testing' + +.. image:: https://img.shields.io/pypi/v/pytest-runner.svg + :target: `PyPI link`_ + +.. image:: https://img.shields.io/pypi/pyversions/pytest-runner.svg + :target: `PyPI link`_ + +.. _PyPI link: https://pypi.org/project/pytest-runner + +.. image:: https://github.com/pytest-dev/pytest-runner/workflows/tests/badge.svg + :target: https://github.com/pytest-dev/pytest-runner/actions?query=workflow%3A%22tests%22 + :alt: tests + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black + :alt: Code style: Black + +.. .. image:: https://readthedocs.org/projects/skeleton/badge/?version=latest +.. :target: https://skeleton.readthedocs.io/en/latest/?badge=latest + +.. image:: https://img.shields.io/badge/skeleton-2022-informational + :target: https://blog.jaraco.com/skeleton + +.. image:: https://tidelift.com/badges/package/pypi/pytest-runner + :target: https://tidelift.com/subscription/pkg/pypi-pytest-runner?utm_source=pypi-pytest-runner&utm_medium=readme + +Setup scripts can use pytest-runner to add setup.py test support for pytest +runner. + +Deprecation Notice +================== + +pytest-runner depends on deprecated features of setuptools and relies on features that break security +mechanisms in pip. For example 'setup_requires' and 'tests_require' bypass ``pip --require-hashes``. +See also `pypa/setuptools#1684 `_. + +It is recommended that you: + +- Remove ``'pytest-runner'`` from your ``setup_requires``, preferably removing the ``setup_requires`` option. +- Remove ``'pytest'`` and any other testing requirements from ``tests_require``, preferably removing the ``tests_requires`` option. +- Select a tool to bootstrap and then run tests such as tox. + +Usage +===== + +- Add 'pytest-runner' to your 'setup_requires'. Pin to '>=2.0,<3dev' (or + similar) to avoid pulling in incompatible versions. +- Include 'pytest' and any other testing requirements to 'tests_require'. +- Invoke tests with ``setup.py pytest``. +- Pass ``--index-url`` to have test requirements downloaded from an alternate + index URL (unnecessary if specified for easy_install in setup.cfg). +- Pass additional py.test command-line options using ``--addopts``. +- Set permanent options for the ``python setup.py pytest`` command (like ``index-url``) + in the ``[pytest]`` section of ``setup.cfg``. +- Set permanent options for the ``py.test`` run (like ``addopts`` or ``pep8ignore``) in the ``[pytest]`` + section of ``pytest.ini`` or ``tox.ini`` or put them in the ``[tool:pytest]`` + section of ``setup.cfg``. See `pytest issue 567 + `_. +- Optionally, set ``test=pytest`` in the ``[aliases]`` section of ``setup.cfg`` + to cause ``python setup.py test`` to invoke pytest. + +Example +======= + +The most simple usage looks like this in setup.py:: + + setup( + setup_requires=[ + 'pytest-runner', + ], + tests_require=[ + 'pytest', + ], + ) + +Additional dependencies require to run the tests (e.g. mock or pytest +plugins) may be added to tests_require and will be downloaded and +required by the session before invoking pytest. + +Follow `this search on github +`_ +for examples of real-world usage. + +Standalone Example +================== + +This technique is deprecated - if you have standalone scripts +you wish to invoke with dependencies, `use pip-run +`_. + +Although ``pytest-runner`` is typically used to add pytest test +runner support to maintained packages, ``pytest-runner`` may +also be used to create standalone tests. Consider `this example +failure `_, +reported in `jsonpickle #117 +`_ +or `this MongoDB test +`_ +demonstrating a technique that works even when dependencies +are required in the test. + +Either example file may be cloned or downloaded and simply run on +any system with Python and Setuptools. It will download the +specified dependencies and run the tests. Afterward, the the +cloned directory can be removed and with it all trace of +invoking the test. No other dependencies are needed and no +system configuration is altered. + +Then, anyone trying to replicate the failure can do so easily +and with all the power of pytest (rewritten assertions, +rich comparisons, interactive debugging, extensibility through +plugins, etc). + +As a result, the communication barrier for describing and +replicating failures is made almost trivially low. + +Considerations +============== + +Conditional Requirement +----------------------- + +Because it uses Setuptools setup_requires, pytest-runner will install itself +on every invocation of setup.py. In some cases, this causes delays for +invocations of setup.py that will never invoke pytest-runner. To help avoid +this contingency, consider requiring pytest-runner only when pytest +is invoked:: + + needs_pytest = {'pytest', 'test', 'ptr'}.intersection(sys.argv) + pytest_runner = ['pytest-runner'] if needs_pytest else [] + + # ... + + setup( + #... + setup_requires=[ + #... (other setup requirements) + ] + pytest_runner, + ) + +For Enterprise +============== + +Available as part of the Tidelift Subscription. + +This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use. + +`Learn more `_. + +Security Contact +================ + +To report a security vulnerability, please use the +`Tidelift security contact `_. +Tidelift will coordinate the fix and disclosure. diff --git a/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/RECORD b/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/RECORD new file mode 100644 index 0000000..d7ff24d --- /dev/null +++ b/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/RECORD @@ -0,0 +1,7 @@ +ptr/__init__.py,sha256=0UfzhCooVgCNTBwVEOPOVGEPck4pnl_6PTfsC-QzNGM,6730 +pytest_runner-6.0.1.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050 +pytest_runner-6.0.1.dist-info/METADATA,sha256=Ho3FvAFjFHeY5OQ64WFzkLigFaIpuNr4G3uSmOk3nho,7319 +pytest_runner-6.0.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92 +pytest_runner-6.0.1.dist-info/entry_points.txt,sha256=BqezBqeO63XyzSYmHYE58gKEFIjJUd-XdsRQkXHy2ig,58 +pytest_runner-6.0.1.dist-info/top_level.txt,sha256=DPzHbWlKG8yq8EOD5UgEvVNDWeJRPyimrwfShwV6Iuw,4 +pytest_runner-6.0.1.dist-info/RECORD,, diff --git a/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/WHEEL b/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/WHEEL new file mode 100644 index 0000000..98c0d20 --- /dev/null +++ b/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.42.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/entry_points.txt b/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/entry_points.txt new file mode 100644 index 0000000..0860670 --- /dev/null +++ b/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/entry_points.txt @@ -0,0 +1,3 @@ +[distutils.commands] +ptr = ptr:PyTest +pytest = ptr:PyTest diff --git a/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/requires.txt b/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/requires.txt new file mode 100644 index 0000000..1535188 --- /dev/null +++ b/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/requires.txt @@ -0,0 +1,17 @@ + +[docs] +sphinx +jaraco.packaging>=9 +rst.linker>=1.9 +jaraco.tidelift>=1.4 + +[testing] +pytest>=6 +pytest-checkdocs>=2.4 +pytest-flake8 +pytest-cov +pytest-enabler>=1.0.1 +pytest-virtualenv +types-setuptools +pytest-black>=0.3.7 +pytest-mypy>=0.9.1 diff --git a/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/top_level.txt b/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/top_level.txt new file mode 100644 index 0000000..e9148ae --- /dev/null +++ b/.eggs/pytest_runner-6.0.1-py3.10.egg/EGG-INFO/top_level.txt @@ -0,0 +1 @@ +ptr diff --git a/.eggs/pytest_runner-6.0.1-py3.10.egg/ptr/__init__.py b/.eggs/pytest_runner-6.0.1-py3.10.egg/ptr/__init__.py new file mode 100644 index 0000000..41192fa --- /dev/null +++ b/.eggs/pytest_runner-6.0.1-py3.10.egg/ptr/__init__.py @@ -0,0 +1,216 @@ +""" +Implementation +""" + +import os as _os +import shlex as _shlex +import contextlib as _contextlib +import sys as _sys +import operator as _operator +import itertools as _itertools +import warnings as _warnings + +import pkg_resources +import setuptools.command.test as orig +from setuptools import Distribution + + +@_contextlib.contextmanager +def _save_argv(repl=None): + saved = _sys.argv[:] + if repl is not None: + _sys.argv[:] = repl + try: + yield saved + finally: + _sys.argv[:] = saved + + +class CustomizedDist(Distribution): + + allow_hosts = None + index_url = None + + def fetch_build_egg(self, req): + """Specialized version of Distribution.fetch_build_egg + that respects respects allow_hosts and index_url.""" + from setuptools.command.easy_install import easy_install + + dist = Distribution({'script_args': ['easy_install']}) + dist.parse_config_files() + opts = dist.get_option_dict('easy_install') + keep = ( + 'find_links', + 'site_dirs', + 'index_url', + 'optimize', + 'site_dirs', + 'allow_hosts', + ) + for key in list(opts): + if key not in keep: + del opts[key] # don't use any other settings + if self.dependency_links: + links = self.dependency_links[:] + if 'find_links' in opts: + links = opts['find_links'][1].split() + links + opts['find_links'] = ('setup', links) + if self.allow_hosts: + opts['allow_hosts'] = ('test', self.allow_hosts) + if self.index_url: + opts['index_url'] = ('test', self.index_url) + install_dir_func = getattr(self, 'get_egg_cache_dir', _os.getcwd) + install_dir = install_dir_func() + cmd = easy_install( + dist, + args=["x"], + install_dir=install_dir, + exclude_scripts=True, + always_copy=False, + build_directory=None, + editable=False, + upgrade=False, + multi_version=True, + no_report=True, + user=False, + ) + cmd.ensure_finalized() + return cmd.easy_install(req) + + +class PyTest(orig.test): + """ + >>> import setuptools + >>> dist = setuptools.Distribution() + >>> cmd = PyTest(dist) + """ + + user_options = [ + ('extras', None, "Install (all) setuptools extras when running tests"), + ( + 'index-url=', + None, + "Specify an index url from which to retrieve dependencies", + ), + ( + 'allow-hosts=', + None, + "Whitelist of comma-separated hosts to allow " + "when retrieving dependencies", + ), + ( + 'addopts=', + None, + "Additional options to be passed verbatim to the pytest runner", + ), + ] + + def initialize_options(self): + self.extras = False + self.index_url = None + self.allow_hosts = None + self.addopts = [] + self.ensure_setuptools_version() + + @staticmethod + def ensure_setuptools_version(): + """ + Due to the fact that pytest-runner is often required (via + setup-requires directive) by toolchains that never invoke + it (i.e. they're only installing the package, not testing it), + instead of declaring the dependency in the package + metadata, assert the requirement at run time. + """ + pkg_resources.require('setuptools>=27.3') + + def finalize_options(self): + if self.addopts: + self.addopts = _shlex.split(self.addopts) + + @staticmethod + def marker_passes(marker): + """ + Given an environment marker, return True if the marker is valid + and matches this environment. + """ + return ( + not marker + or not pkg_resources.invalid_marker(marker) + and pkg_resources.evaluate_marker(marker) + ) + + def install_dists(self, dist): + """ + Extend install_dists to include extras support + """ + return _itertools.chain( + orig.test.install_dists(dist), self.install_extra_dists(dist) + ) + + def install_extra_dists(self, dist): + """ + Install extras that are indicated by markers or + install all extras if '--extras' is indicated. + """ + extras_require = dist.extras_require or {} + + spec_extras = ( + (spec.partition(':'), reqs) for spec, reqs in extras_require.items() + ) + matching_extras = ( + reqs + for (name, sep, marker), reqs in spec_extras + # include unnamed extras or all if self.extras indicated + if (not name or self.extras) + # never include extras that fail to pass marker eval + and self.marker_passes(marker) + ) + results = list(map(dist.fetch_build_eggs, matching_extras)) + return _itertools.chain.from_iterable(results) + + @staticmethod + def _warn_old_setuptools(): + msg = ( + "pytest-runner will stop working on this version of setuptools; " + "please upgrade to setuptools 30.4 or later or pin to " + "pytest-runner < 5." + ) + ver_str = pkg_resources.get_distribution('setuptools').version + ver = pkg_resources.parse_version(ver_str) + if ver < pkg_resources.parse_version('30.4'): + _warnings.warn(msg) + + def run(self): + """ + Override run to ensure requirements are available in this session (but + don't install them anywhere). + """ + self._warn_old_setuptools() + dist = CustomizedDist() + for attr in 'allow_hosts index_url'.split(): + setattr(dist, attr, getattr(self, attr)) + for attr in ( + 'dependency_links install_requires tests_require extras_require ' + ).split(): + setattr(dist, attr, getattr(self.distribution, attr)) + installed_dists = self.install_dists(dist) + if self.dry_run: + self.announce('skipping tests (dry run)') + return + paths = map(_operator.attrgetter('location'), installed_dists) + with self.paths_on_pythonpath(paths): + with self.project_on_sys_path(): + return self.run_tests() + + @property + def _argv(self): + return ['pytest'] + self.addopts + + def run_tests(self): + """ + Invoke pytest, replacing argv. Return result code. + """ + with _save_argv(_sys.argv[:1] + self.addopts): + result_code = __import__('pytest').main() + if result_code: + raise SystemExit(result_code) diff --git a/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/LICENSE b/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/LICENSE new file mode 100644 index 0000000..353924b --- /dev/null +++ b/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/LICENSE @@ -0,0 +1,19 @@ +Copyright Jason R. Coombs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/PKG-INFO b/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/PKG-INFO new file mode 100644 index 0000000..75c36d6 --- /dev/null +++ b/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/PKG-INFO @@ -0,0 +1,185 @@ +Metadata-Version: 2.1 +Name: pytest-runner +Version: 6.0.1 +Summary: Invoke py.test as distutils command with dependency resolution +Home-page: https://github.com/pytest-dev/pytest-runner/ +Author: Jason R. Coombs +Author-email: jaraco@jaraco.com +Classifier: Development Status :: 7 - Inactive +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Framework :: Pytest +Requires-Python: >=3.7 +License-File: LICENSE +Provides-Extra: docs +Requires-Dist: sphinx ; extra == 'docs' +Requires-Dist: jaraco.packaging >=9 ; extra == 'docs' +Requires-Dist: rst.linker >=1.9 ; extra == 'docs' +Requires-Dist: jaraco.tidelift >=1.4 ; extra == 'docs' +Provides-Extra: testing +Requires-Dist: pytest >=6 ; extra == 'testing' +Requires-Dist: pytest-checkdocs >=2.4 ; extra == 'testing' +Requires-Dist: pytest-flake8 ; extra == 'testing' +Requires-Dist: pytest-cov ; extra == 'testing' +Requires-Dist: pytest-enabler >=1.0.1 ; extra == 'testing' +Requires-Dist: pytest-virtualenv ; extra == 'testing' +Requires-Dist: types-setuptools ; extra == 'testing' +Requires-Dist: pytest-black >=0.3.7 ; (platform_python_implementation != "PyPy") and extra == 'testing' +Requires-Dist: pytest-mypy >=0.9.1 ; (platform_python_implementation != "PyPy") and extra == 'testing' + +.. image:: https://img.shields.io/pypi/v/pytest-runner.svg + :target: `PyPI link`_ + +.. image:: https://img.shields.io/pypi/pyversions/pytest-runner.svg + :target: `PyPI link`_ + +.. _PyPI link: https://pypi.org/project/pytest-runner + +.. image:: https://github.com/pytest-dev/pytest-runner/workflows/tests/badge.svg + :target: https://github.com/pytest-dev/pytest-runner/actions?query=workflow%3A%22tests%22 + :alt: tests + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black + :alt: Code style: Black + +.. .. image:: https://readthedocs.org/projects/skeleton/badge/?version=latest +.. :target: https://skeleton.readthedocs.io/en/latest/?badge=latest + +.. image:: https://img.shields.io/badge/skeleton-2022-informational + :target: https://blog.jaraco.com/skeleton + +.. image:: https://tidelift.com/badges/package/pypi/pytest-runner + :target: https://tidelift.com/subscription/pkg/pypi-pytest-runner?utm_source=pypi-pytest-runner&utm_medium=readme + +Setup scripts can use pytest-runner to add setup.py test support for pytest +runner. + +Deprecation Notice +================== + +pytest-runner depends on deprecated features of setuptools and relies on features that break security +mechanisms in pip. For example 'setup_requires' and 'tests_require' bypass ``pip --require-hashes``. +See also `pypa/setuptools#1684 `_. + +It is recommended that you: + +- Remove ``'pytest-runner'`` from your ``setup_requires``, preferably removing the ``setup_requires`` option. +- Remove ``'pytest'`` and any other testing requirements from ``tests_require``, preferably removing the ``tests_requires`` option. +- Select a tool to bootstrap and then run tests such as tox. + +Usage +===== + +- Add 'pytest-runner' to your 'setup_requires'. Pin to '>=2.0,<3dev' (or + similar) to avoid pulling in incompatible versions. +- Include 'pytest' and any other testing requirements to 'tests_require'. +- Invoke tests with ``setup.py pytest``. +- Pass ``--index-url`` to have test requirements downloaded from an alternate + index URL (unnecessary if specified for easy_install in setup.cfg). +- Pass additional py.test command-line options using ``--addopts``. +- Set permanent options for the ``python setup.py pytest`` command (like ``index-url``) + in the ``[pytest]`` section of ``setup.cfg``. +- Set permanent options for the ``py.test`` run (like ``addopts`` or ``pep8ignore``) in the ``[pytest]`` + section of ``pytest.ini`` or ``tox.ini`` or put them in the ``[tool:pytest]`` + section of ``setup.cfg``. See `pytest issue 567 + `_. +- Optionally, set ``test=pytest`` in the ``[aliases]`` section of ``setup.cfg`` + to cause ``python setup.py test`` to invoke pytest. + +Example +======= + +The most simple usage looks like this in setup.py:: + + setup( + setup_requires=[ + 'pytest-runner', + ], + tests_require=[ + 'pytest', + ], + ) + +Additional dependencies require to run the tests (e.g. mock or pytest +plugins) may be added to tests_require and will be downloaded and +required by the session before invoking pytest. + +Follow `this search on github +`_ +for examples of real-world usage. + +Standalone Example +================== + +This technique is deprecated - if you have standalone scripts +you wish to invoke with dependencies, `use pip-run +`_. + +Although ``pytest-runner`` is typically used to add pytest test +runner support to maintained packages, ``pytest-runner`` may +also be used to create standalone tests. Consider `this example +failure `_, +reported in `jsonpickle #117 +`_ +or `this MongoDB test +`_ +demonstrating a technique that works even when dependencies +are required in the test. + +Either example file may be cloned or downloaded and simply run on +any system with Python and Setuptools. It will download the +specified dependencies and run the tests. Afterward, the the +cloned directory can be removed and with it all trace of +invoking the test. No other dependencies are needed and no +system configuration is altered. + +Then, anyone trying to replicate the failure can do so easily +and with all the power of pytest (rewritten assertions, +rich comparisons, interactive debugging, extensibility through +plugins, etc). + +As a result, the communication barrier for describing and +replicating failures is made almost trivially low. + +Considerations +============== + +Conditional Requirement +----------------------- + +Because it uses Setuptools setup_requires, pytest-runner will install itself +on every invocation of setup.py. In some cases, this causes delays for +invocations of setup.py that will never invoke pytest-runner. To help avoid +this contingency, consider requiring pytest-runner only when pytest +is invoked:: + + needs_pytest = {'pytest', 'test', 'ptr'}.intersection(sys.argv) + pytest_runner = ['pytest-runner'] if needs_pytest else [] + + # ... + + setup( + #... + setup_requires=[ + #... (other setup requirements) + ] + pytest_runner, + ) + +For Enterprise +============== + +Available as part of the Tidelift Subscription. + +This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use. + +`Learn more `_. + +Security Contact +================ + +To report a security vulnerability, please use the +`Tidelift security contact `_. +Tidelift will coordinate the fix and disclosure. diff --git a/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/RECORD b/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/RECORD new file mode 100644 index 0000000..d7ff24d --- /dev/null +++ b/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/RECORD @@ -0,0 +1,7 @@ +ptr/__init__.py,sha256=0UfzhCooVgCNTBwVEOPOVGEPck4pnl_6PTfsC-QzNGM,6730 +pytest_runner-6.0.1.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050 +pytest_runner-6.0.1.dist-info/METADATA,sha256=Ho3FvAFjFHeY5OQ64WFzkLigFaIpuNr4G3uSmOk3nho,7319 +pytest_runner-6.0.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92 +pytest_runner-6.0.1.dist-info/entry_points.txt,sha256=BqezBqeO63XyzSYmHYE58gKEFIjJUd-XdsRQkXHy2ig,58 +pytest_runner-6.0.1.dist-info/top_level.txt,sha256=DPzHbWlKG8yq8EOD5UgEvVNDWeJRPyimrwfShwV6Iuw,4 +pytest_runner-6.0.1.dist-info/RECORD,, diff --git a/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/WHEEL b/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/WHEEL new file mode 100644 index 0000000..98c0d20 --- /dev/null +++ b/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.42.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/entry_points.txt b/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/entry_points.txt new file mode 100644 index 0000000..0860670 --- /dev/null +++ b/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/entry_points.txt @@ -0,0 +1,3 @@ +[distutils.commands] +ptr = ptr:PyTest +pytest = ptr:PyTest diff --git a/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/requires.txt b/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/requires.txt new file mode 100644 index 0000000..1535188 --- /dev/null +++ b/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/requires.txt @@ -0,0 +1,17 @@ + +[docs] +sphinx +jaraco.packaging>=9 +rst.linker>=1.9 +jaraco.tidelift>=1.4 + +[testing] +pytest>=6 +pytest-checkdocs>=2.4 +pytest-flake8 +pytest-cov +pytest-enabler>=1.0.1 +pytest-virtualenv +types-setuptools +pytest-black>=0.3.7 +pytest-mypy>=0.9.1 diff --git a/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/top_level.txt b/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/top_level.txt new file mode 100644 index 0000000..e9148ae --- /dev/null +++ b/.eggs/pytest_runner-6.0.1-py3.9.egg/EGG-INFO/top_level.txt @@ -0,0 +1 @@ +ptr diff --git a/.eggs/pytest_runner-6.0.1-py3.9.egg/ptr/__init__.py b/.eggs/pytest_runner-6.0.1-py3.9.egg/ptr/__init__.py new file mode 100644 index 0000000..41192fa --- /dev/null +++ b/.eggs/pytest_runner-6.0.1-py3.9.egg/ptr/__init__.py @@ -0,0 +1,216 @@ +""" +Implementation +""" + +import os as _os +import shlex as _shlex +import contextlib as _contextlib +import sys as _sys +import operator as _operator +import itertools as _itertools +import warnings as _warnings + +import pkg_resources +import setuptools.command.test as orig +from setuptools import Distribution + + +@_contextlib.contextmanager +def _save_argv(repl=None): + saved = _sys.argv[:] + if repl is not None: + _sys.argv[:] = repl + try: + yield saved + finally: + _sys.argv[:] = saved + + +class CustomizedDist(Distribution): + + allow_hosts = None + index_url = None + + def fetch_build_egg(self, req): + """Specialized version of Distribution.fetch_build_egg + that respects respects allow_hosts and index_url.""" + from setuptools.command.easy_install import easy_install + + dist = Distribution({'script_args': ['easy_install']}) + dist.parse_config_files() + opts = dist.get_option_dict('easy_install') + keep = ( + 'find_links', + 'site_dirs', + 'index_url', + 'optimize', + 'site_dirs', + 'allow_hosts', + ) + for key in list(opts): + if key not in keep: + del opts[key] # don't use any other settings + if self.dependency_links: + links = self.dependency_links[:] + if 'find_links' in opts: + links = opts['find_links'][1].split() + links + opts['find_links'] = ('setup', links) + if self.allow_hosts: + opts['allow_hosts'] = ('test', self.allow_hosts) + if self.index_url: + opts['index_url'] = ('test', self.index_url) + install_dir_func = getattr(self, 'get_egg_cache_dir', _os.getcwd) + install_dir = install_dir_func() + cmd = easy_install( + dist, + args=["x"], + install_dir=install_dir, + exclude_scripts=True, + always_copy=False, + build_directory=None, + editable=False, + upgrade=False, + multi_version=True, + no_report=True, + user=False, + ) + cmd.ensure_finalized() + return cmd.easy_install(req) + + +class PyTest(orig.test): + """ + >>> import setuptools + >>> dist = setuptools.Distribution() + >>> cmd = PyTest(dist) + """ + + user_options = [ + ('extras', None, "Install (all) setuptools extras when running tests"), + ( + 'index-url=', + None, + "Specify an index url from which to retrieve dependencies", + ), + ( + 'allow-hosts=', + None, + "Whitelist of comma-separated hosts to allow " + "when retrieving dependencies", + ), + ( + 'addopts=', + None, + "Additional options to be passed verbatim to the pytest runner", + ), + ] + + def initialize_options(self): + self.extras = False + self.index_url = None + self.allow_hosts = None + self.addopts = [] + self.ensure_setuptools_version() + + @staticmethod + def ensure_setuptools_version(): + """ + Due to the fact that pytest-runner is often required (via + setup-requires directive) by toolchains that never invoke + it (i.e. they're only installing the package, not testing it), + instead of declaring the dependency in the package + metadata, assert the requirement at run time. + """ + pkg_resources.require('setuptools>=27.3') + + def finalize_options(self): + if self.addopts: + self.addopts = _shlex.split(self.addopts) + + @staticmethod + def marker_passes(marker): + """ + Given an environment marker, return True if the marker is valid + and matches this environment. + """ + return ( + not marker + or not pkg_resources.invalid_marker(marker) + and pkg_resources.evaluate_marker(marker) + ) + + def install_dists(self, dist): + """ + Extend install_dists to include extras support + """ + return _itertools.chain( + orig.test.install_dists(dist), self.install_extra_dists(dist) + ) + + def install_extra_dists(self, dist): + """ + Install extras that are indicated by markers or + install all extras if '--extras' is indicated. + """ + extras_require = dist.extras_require or {} + + spec_extras = ( + (spec.partition(':'), reqs) for spec, reqs in extras_require.items() + ) + matching_extras = ( + reqs + for (name, sep, marker), reqs in spec_extras + # include unnamed extras or all if self.extras indicated + if (not name or self.extras) + # never include extras that fail to pass marker eval + and self.marker_passes(marker) + ) + results = list(map(dist.fetch_build_eggs, matching_extras)) + return _itertools.chain.from_iterable(results) + + @staticmethod + def _warn_old_setuptools(): + msg = ( + "pytest-runner will stop working on this version of setuptools; " + "please upgrade to setuptools 30.4 or later or pin to " + "pytest-runner < 5." + ) + ver_str = pkg_resources.get_distribution('setuptools').version + ver = pkg_resources.parse_version(ver_str) + if ver < pkg_resources.parse_version('30.4'): + _warnings.warn(msg) + + def run(self): + """ + Override run to ensure requirements are available in this session (but + don't install them anywhere). + """ + self._warn_old_setuptools() + dist = CustomizedDist() + for attr in 'allow_hosts index_url'.split(): + setattr(dist, attr, getattr(self, attr)) + for attr in ( + 'dependency_links install_requires tests_require extras_require ' + ).split(): + setattr(dist, attr, getattr(self.distribution, attr)) + installed_dists = self.install_dists(dist) + if self.dry_run: + self.announce('skipping tests (dry run)') + return + paths = map(_operator.attrgetter('location'), installed_dists) + with self.paths_on_pythonpath(paths): + with self.project_on_sys_path(): + return self.run_tests() + + @property + def _argv(self): + return ['pytest'] + self.addopts + + def run_tests(self): + """ + Invoke pytest, replacing argv. Return result code. + """ + with _save_argv(_sys.argv[:1] + self.addopts): + result_code = __import__('pytest').main() + if result_code: + raise SystemExit(result_code) diff --git a/.gitignore b/.gitignore index bee8a64..d34b716 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,61 @@ -__pycache__ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +env/ +venv/ +ENV/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# Django +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal +/media +/staticfiles + +# Environment +.env +.venv + +# Node +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* +dist/ +dist-ssr/ +*.local + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Build +build/ +*.egg-info/ diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/Time-Table.iml b/.idea/Time-Table.iml new file mode 100644 index 0000000..cc3085d --- /dev/null +++ b/.idea/Time-Table.iml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..a136806 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,24 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..f0779c8 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..84e9391 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Constants/Teacher's chromo.json b/Constants/Teacher's chromo.json deleted file mode 100644 index aa5f1ea..0000000 --- a/Constants/Teacher's chromo.json +++ /dev/null @@ -1,277 +0,0 @@ -{ - "Teacher1": { - "Monday": [ - { - "course_name": "BTech", - "subject_id": "TCS-509", - "classroom_name": "R1", - "time_slot": "9:00 - 9:55", - "section": "A" - }, - { - "course_name": "BTech", - "subject_id": "TCS-509", - "classroom_name": "R1", - "time_slot": "10:00 - 10:55", - "section": "A" - } - ], - "Tuesday": [ - { - "course_name": "BCA", - "subject_id": "CSP-501", - "classroom_name": "L2", - "time_slot": "9:00 - 9:55", - "section": "B" - }, - { - "course_name": "BTech", - "subject_id": "TCS-509", - "classroom_name": "R1", - "time_slot": "11:00 - 11:55", - "section": "A" - } - ], - "Wednesday": [ - { - "course_name": "MBA", - "subject_id": "MKT-202", - "classroom_name": "R3", - "time_slot": "9:00 - 9:55", - "section": "A" - }, - { - "course_name": "BCA", - "subject_id": "CSP-501", - "classroom_name": "L2", - "time_slot": "10:00 - 10:55", - "section": "B" - } - ], - "Thursday": [ - { - "course_name": "BTech", - "subject_id": "TCS-509", - "classroom_name": "R1", - "time_slot": "9:00 - 9:55", - "section": "A" - }, - { - "course_name": "MBA", - "subject_id": "MKT-202", - "classroom_name": "R3", - "time_slot": "11:00 - 11:55", - "section": "A" - }, - { - "course_name": "BTech", - "subject_id": "TCS-509", - "classroom_name": "R1", - "time_slot": "2:00 - 2:55", - "section": "B" - } - ], - "Friday": [ - { - "course_name": "BCA", - "subject_id": "CSP-501", - "classroom_name": "L2", - "time_slot": "9:00 - 9:55", - "section": "B" - }, - { - "course_name": "MBA", - "subject_id": "MKT-202", - "classroom_name": "R3", - "time_slot": "11:00 - 11:55", - "section": "A" - }, - { - "course_name": "BTech", - "subject_id": "TCS-509", - "classroom_name": "R1", - "time_slot": "2:00 - 2:55", - "section": "A" - } - ] - }, - "Teacher2": { - "Tuesday": [ - { - "course_name": "BCA", - "subject_id": "CSP-501", - "classroom_name": "L2", - "time_slot": "9:00 - 9:55", - "section": "B" - }, - { - "course_name": "BTech", - "subject_id": "TCS-509", - "classroom_name": "R1", - "time_slot": "11:00 - 11:55", - "section": "A" - } - ], - "Wednesday": [ - { - "course_name": "MBA", - "subject_id": "MKT-202", - "classroom_name": "R3", - "time_slot": "9:00 - 9:55", - "section": "A" - }, - { - "course_name": "BCA", - "subject_id": "CSP-501", - "classroom_name": "L2", - "time_slot": "10:00 - 10:55", - "section": "B" - } - ], - "Thursday": [ - { - "course_name": "BTech", - "subject_id": "TCS-509", - "classroom_name": "R1", - "time_slot": "9:00 - 9:55", - "section": "A" - }, - { - "course_name": "MBA", - "subject_id": "MKT-202", - "classroom_name": "R3", - "time_slot": "11:00 - 11:55", - "section": "A" - }, - { - "course_name": "BTech", - "subject_id": "TCS-509", - "classroom_name": "R1", - "time_slot": "2:00 - 2:55", - "section": "B" - } - ], - "Friday": [ - { - "course_name": "BCA", - "subject_id": "CSP-501", - "classroom_name": "L2", - "time_slot": "9:00 - 9:55", - "section": "B" - }, - { - "course_name": "MBA", - "subject_id": "MKT-202", - "classroom_name": "R3", - "time_slot": "11:00 - 11:55", - "section": "A" - }, - { - "course_name": "BTech", - "subject_id": "TCS-509", - "classroom_name": "R1", - "time_slot": "2:00 - 2:55", - "section": "A" - } - ], - "Saturday": [ - { - "course_name": "BTech", - "subject_id": "TCS-509", - "classroom_name": "R1", - "time_slot": "9:00 - 9:55", - "section": "A" - }, - { - "course_name": "BTech", - "subject_id": "TCS-509", - "classroom_name": "R1", - "time_slot": "10:00 - 10:55", - "section": "A" - } - ] - }, - "Teacher3": { - "Monday": [ - { - "course_name": "BBA", - "subject_id": "HRM-301", - "classroom_name": "L3", - "time_slot": "9:00 - 9:55", - "section": "C" - }, - { - "course_name": "MBA", - "subject_id": "FIN-305", - "classroom_name": "R4", - "time_slot": "11:00 - 11:55", - "section": "A" - } - ], - "Tuesday": [ - { - "course_name": "BTech", - "subject_id": "TCS-510", - "classroom_name": "R2", - "time_slot": "10:00 - 10:55", - "section": "A" - }, - { - "course_name": "BCA", - "subject_id": "CSP-502", - "classroom_name": "L1", - "time_slot": "2:00 - 2:55", - "section": "B" - } - ], - "Wednesday": [ - { - "course_name": "MBA", - "subject_id": "MKT-203", - "classroom_name": "R3", - "time_slot": "10:00 - 10:55", - "section": "A" - }, - { - "course_name": "BBA", - "subject_id": "HRM-301", - "classroom_name": "L3", - "time_slot": "2:00 - 2:55", - "section": "C" - } - ] - }, - "Teacher4": { - "Thursday": [ - { - "course_name": "BCA", - "subject_id": "CSP-503", - "classroom_name": "L1", - "time_slot": "9:00 - 9:55", - "section": "B" - }, - { - "course_name": "BTech", - "subject_id": "TCS-511", - "classroom_name": "R2", - "time_slot": "11:00 - 11:55", - "section": "C" - } - ], - "Friday": [ - { - "course_name": "MBA", - "subject_id": "FIN-306", - "classroom_name": "R4", - "time_slot": "9:00 - 9:55", - "section": "A" - }, - { - "course_name": "BBA", - "subject_id": "HRM-302", - "classroom_name": "L3", - "time_slot": "10:00 - 10:55", - } - ] - } -} \ No newline at end of file diff --git a/GA/classroom_allocate.py b/Constants/classroom_allocate.py similarity index 78% rename from GA/classroom_allocate.py rename to Constants/classroom_allocate.py index 238c1d2..5030f56 100644 --- a/GA/classroom_allocate.py +++ b/Constants/classroom_allocate.py @@ -1,15 +1,16 @@ from ortools.sat.python import cp_model + def assign_section_to_classes(x_section, y_classrooms, z_time_slots): # Create a CP-SAT model model = cp_model.CpModel() # Decision variables classrooms = [ - model.NewIntVar(0, y_classrooms - 1, f'classroom_{i}') for i in range(x_section) + model.NewIntVar(0, y_classrooms - 1, f"classroom_{i}") for i in range(x_section) ] timeslots = [ - model.NewIntVar(0, z_time_slots - 1, f'timeslot_{i}') for i in range(x_section) + model.NewIntVar(0, z_time_slots - 1, f"timeslot_{i}") for i in range(x_section) ] # Constraints @@ -17,14 +18,13 @@ def assign_section_to_classes(x_section, y_classrooms, z_time_slots): for i in range(x_section): for j in range(i + 1, x_section): # Define a Boolean variable for the condition (timeslots[i] == timeslots[j]) - same_timeslot = model.NewBoolVar(f'same_timeslot_{i}_{j}') + same_timeslot = model.NewBoolVar(f"same_timeslot_{i}_{j}") model.Add(timeslots[i] == timeslots[j]).OnlyEnforceIf(same_timeslot) model.Add(timeslots[i] != timeslots[j]).OnlyEnforceIf(same_timeslot.Not()) # Enforce classroom difference if the timeslot is the same model.Add(classrooms[i] != classrooms[j]).OnlyEnforceIf(same_timeslot) - # Solver solver = cp_model.CpSolver() status = solver.Solve(model) @@ -34,21 +34,23 @@ def assign_section_to_classes(x_section, y_classrooms, z_time_slots): for i in range(x_section): assigned_classroom = solver.Value(classrooms[i]) assigned_timeslot = solver.Value(timeslots[i]) - print(f"Subject {i + 1} -> Classroom {assigned_classroom}, Timeslot {assigned_timeslot}") + print( + f"Subject {i + 1} -> Classroom {assigned_classroom}, Timeslot {assigned_timeslot}" + ) else: print("No feasible solution found.") -from datetime import datetime +from datetime import datetime start_time = datetime.now() X = 80 # Number of section Y = 50 # Number of classrooms -Z = 7 # Number of time slots +Z = 7 # Number of time slots assign_section_to_classes(X, Y, Z) end_time = datetime.now() -print(end_time - start_time) \ No newline at end of file +print(end_time - start_time) diff --git a/Constants/classroom_timetable.json b/Constants/classroom_timetable.json new file mode 100644 index 0000000..983356b --- /dev/null +++ b/Constants/classroom_timetable.json @@ -0,0 +1,2255 @@ +{ + "L2": { + "Monday": { + "A": [ + { + "subject_id": "PCS-503", + "teacher_id": "RS11", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PCS-503", + "teacher_id": "RS11", + "time_slot": "09:00 - 10:00" + } + ], + "D": [ + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "14:50 - 15:50" + } + ], + "F": [ + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "14:50 - 15:50" + } + ] + }, + "Tuesday": { + "A": [ + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "09:00 - 10:00" + } + ], + "F": [ + { + "subject_id": "PCS-506", + "teacher_id": "RD09", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-506", + "teacher_id": "RD09", + "time_slot": "14:50 - 15:50" + } + ] + }, + "Wednesday": { + "D": [ + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "14:50 - 15:50" + } + ], + "E": [ + { + "subject_id": "PCS-503", + "teacher_id": "VD25", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-503", + "teacher_id": "VD25", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "PMA-502", + "teacher_id": "AD08", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PMA-502", + "teacher_id": "AD08", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Thursday": { + "C": [ + { + "subject_id": "PCS-503", + "teacher_id": "DP07", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PCS-503", + "teacher_id": "DP07", + "time_slot": "09:00 - 10:00" + } + ], + "D": [ + { + "subject_id": "PCS-503", + "teacher_id": "VD25", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-503", + "teacher_id": "VD25", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "PMA-502", + "teacher_id": "AD08", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PMA-502", + "teacher_id": "AD08", + "time_slot": "09:00 - 10:00" + } + ], + "E": [ + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "14:50 - 15:50" + } + ], + "F": [ + { + "subject_id": "PCS-503", + "teacher_id": "RS11", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PCS-503", + "teacher_id": "RS11", + "time_slot": "09:00 - 10:00" + } + ], + "B": [ + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Friday": { + "B": [ + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "09:00 - 10:00" + } + ], + "C": [ + { + "subject_id": "PCS-503", + "teacher_id": "DP07", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PCS-503", + "teacher_id": "DP07", + "time_slot": "09:00 - 10:00" + } + ], + "D": [ + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Saturday": {} + }, + "L1": { + "Monday": { + "A": [ + { + "subject_id": "PCS-503", + "teacher_id": "RS11", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-503", + "teacher_id": "RS11", + "time_slot": "12:00 - 13:00" + } + ], + "D": [ + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "12:00 - 13:00" + } + ], + "C": [ + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "12:00 - 13:00" + } + ], + "F": [ + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Tuesday": { + "A": [ + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "12:00 - 13:00" + } + ], + "C": [ + { + "subject_id": "PMA-502", + "teacher_id": "AD08", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PMA-502", + "teacher_id": "AD08", + "time_slot": "12:00 - 13:00" + } + ], + "F": [ + { + "subject_id": "PCS-506", + "teacher_id": "RD09", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-506", + "teacher_id": "RD09", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Wednesday": { + "C": [ + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "12:00 - 13:00" + } + ], + "D": [ + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "12:00 - 13:00" + } + ], + "E": [ + { + "subject_id": "PCS-503", + "teacher_id": "VD25", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-503", + "teacher_id": "VD25", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "PMA-502", + "teacher_id": "AD08", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PMA-502", + "teacher_id": "AD08", + "time_slot": "12:00 - 13:00" + } + ], + "A": [ + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Thursday": { + "C": [ + { + "subject_id": "PCS-503", + "teacher_id": "DP07", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-503", + "teacher_id": "DP07", + "time_slot": "12:00 - 13:00" + } + ], + "D": [ + { + "subject_id": "PCS-503", + "teacher_id": "VD25", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-503", + "teacher_id": "VD25", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "PMA-502", + "teacher_id": "AD08", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PMA-502", + "teacher_id": "AD08", + "time_slot": "12:00 - 13:00" + } + ], + "E": [ + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "12:00 - 13:00" + } + ], + "F": [ + { + "subject_id": "PCS-503", + "teacher_id": "RS11", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-503", + "teacher_id": "RS11", + "time_slot": "12:00 - 13:00" + } + ], + "A": [ + { + "subject_id": "PCS-503", + "teacher_id": "RS11", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-503", + "teacher_id": "RS11", + "time_slot": "12:00 - 13:00" + } + ], + "B": [ + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Friday": { + "B": [ + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "12:00 - 13:00" + } + ], + "C": [ + { + "subject_id": "PCS-503", + "teacher_id": "DP07", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-503", + "teacher_id": "DP07", + "time_slot": "12:00 - 13:00" + } + ], + "D": [ + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-506", + "teacher_id": "AD08", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Saturday": {} + }, + "R2": { + "Monday": { + "B": [ + { + "subject_id": "XCS-501", + "teacher_id": "DT20", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "AB01", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "SCS-501", + "teacher_id": "AP24", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "BJ10", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "AB01", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "SCS-501", + "teacher_id": "AP24", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "CSP-501", + "teacher_id": "AK23", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "BJ10", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Tuesday": { + "B": [ + { + "subject_id": "TCS-503", + "teacher_id": "SP06", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "AB01", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "XCS-501", + "teacher_id": "DT20", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TCS-502", + "teacher_id": "SS03", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "AB01", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "XCS-501", + "teacher_id": "DT20", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "SP06", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TCS-502", + "teacher_id": "SS03", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Wednesday": { + "B": [ + { + "subject_id": "CSP-501", + "teacher_id": "AK23", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "DP07", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "AB17", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TCS-502", + "teacher_id": "SS03", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "SP06", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "AB01", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-502", + "teacher_id": "SS03", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "SJ16", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Thursday": { + "B": [ + { + "subject_id": "TMA-502", + "teacher_id": "RS11", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "AB17", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "SP06", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "AB01", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Friday": { + "B": [ + { + "subject_id": "XCS-501", + "teacher_id": "DT20", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "SP06", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "RS11", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TCS-502", + "teacher_id": "AA04", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Saturday": { + "B": [ + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "12:00 - 13:00" + } + ] + } + }, + "R3": { + "Monday": { + "C": [ + { + "subject_id": "CSP-501", + "teacher_id": "AK23", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-502", + "teacher_id": "SS03", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "SCS-501", + "teacher_id": "AP24", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "XCS-501", + "teacher_id": "PA21", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "DP07", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "CSP-501", + "teacher_id": "AK23", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Tuesday": { + "C": [ + { + "subject_id": "TCS-502", + "teacher_id": "AA04", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "DP07", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-502", + "teacher_id": "AA04", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "SJ16", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "BJ10", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "SCS-501", + "teacher_id": "AP24", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Wednesday": { + "C": [ + { + "subject_id": "TCS-531", + "teacher_id": "PK02", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-502", + "teacher_id": "AA04", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "DP07", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "RS11", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "PK02", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "AB17", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Thursday": { + "C": [ + { + "subject_id": "TCS-502", + "teacher_id": "SS03", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "SP06", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "AB17", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "JM12", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Friday": { + "C": [ + { + "subject_id": "TCS-509", + "teacher_id": "SJ16", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "DP07", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "RS11", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "PK02", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Saturday": { + "C": [ + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "12:00 - 13:00" + } + ] + } + }, + "R4": { + "Monday": { + "D": [ + { + "subject_id": "CSP-501", + "teacher_id": "AK23", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "SP06", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "XCS-501", + "teacher_id": "NB22", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "SCS-501", + "teacher_id": "AP24", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "RS11", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "AB17", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "CSP-501", + "teacher_id": "AK23", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "PK02", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "TCS-503", + "teacher_id": "AC05", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "TCS-502", + "teacher_id": "SS03", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Tuesday": { + "D": [ + { + "subject_id": "XCS-501", + "teacher_id": "PA21", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "SJ16", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "SCS-501", + "teacher_id": "AP24", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "AC05", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "BJ10", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "TCS-502", + "teacher_id": "SS03", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "TCS-531", + "teacher_id": "PK02", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "XCS-501", + "teacher_id": "PA21", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "PK02", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "RS11", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "AB17", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-502", + "teacher_id": "AC05", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "TCS-503", + "teacher_id": "DP07", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Wednesday": { + "D": [ + { + "subject_id": "TCS-509", + "teacher_id": "HP18", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "AC05", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "BJ10", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "TCS-531", + "teacher_id": "AB01", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "XCS-501", + "teacher_id": "PA21", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "HP18", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "JM12", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "AC05", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Thursday": { + "D": [ + { + "subject_id": "TCS-502", + "teacher_id": "SS03", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "PK02", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "HP18", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "Placement_Class", + "teacher_id": "AK26", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Placement_Class", + "teacher_id": "AK26", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "TCS-502", + "teacher_id": "AA04", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Friday": { + "D": [ + { + "subject_id": "TCS-502", + "teacher_id": "SS03", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "AB01", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "JM12", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Placement_Class", + "teacher_id": "AK26", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Placement_Class", + "teacher_id": "AK26", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Saturday": { + "D": [ + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "16:50 - 17:50" + } + ] + } + }, + "R5": { + "Monday": { + "E": [ + { + "subject_id": "TCS-531", + "teacher_id": "PK02", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-502", + "teacher_id": "AA04", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "CSP-501", + "teacher_id": "AK23", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "XCS-501", + "teacher_id": "DT20", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "DP07", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "TCS-509", + "teacher_id": "SJ16", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "SCS-501", + "teacher_id": "AP24", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "XCS-501", + "teacher_id": "DT20", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "SP06", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "SCS-501", + "teacher_id": "AP24", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "JM12", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Placement_Class", + "teacher_id": "AK26", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Placement_Class", + "teacher_id": "AK26", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "TCS-509", + "teacher_id": "HP18", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Tuesday": { + "E": [ + { + "subject_id": "TCS-502", + "teacher_id": "AA04", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "RS11", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "XCS-501", + "teacher_id": "NB22", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "AB01", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "AB17", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "CSP-501", + "teacher_id": "AK23", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "SP06", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-502", + "teacher_id": "SS03", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "XCS-501", + "teacher_id": "NB22", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "AB01", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Wednesday": { + "E": [ + { + "subject_id": "TMA-502", + "teacher_id": "RS11", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "AB01", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "SG19", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "TCS-509", + "teacher_id": "SG19", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "TCS-503", + "teacher_id": "SP06", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "TCS-502", + "teacher_id": "AA04", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Thursday": { + "E": [ + { + "subject_id": "TCS-503", + "teacher_id": "AC05", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-502", + "teacher_id": "AA04", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "JM12", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "TCS-531", + "teacher_id": "AB01", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-502", + "teacher_id": "AC05", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "HP18", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "NJ13", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Friday": { + "E": [ + { + "subject_id": "TCS-503", + "teacher_id": "AC05", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Placement_Class", + "teacher_id": "AK26", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Placement_Class", + "teacher_id": "AK26", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "TCS-531", + "teacher_id": "PK02", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "JM12", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Saturday": { + "E": [ + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "16:50 - 17:50" + } + ] + } + }, + "R6": { + "Monday": { + "F": [ + { + "subject_id": "CSP-501", + "teacher_id": "AK23", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "AB17", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "SCS-501", + "teacher_id": "AP24", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "XCS-501", + "teacher_id": "PA21", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "AC05", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "SCS-501", + "teacher_id": "AP24", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "DP07", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "XCS-501", + "teacher_id": "PA21", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Tuesday": { + "F": [ + { + "subject_id": "TCS-502", + "teacher_id": "AC05", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "PK02", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "SP06", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "XCS-501", + "teacher_id": "DT20", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "DP07", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "JM12", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "CSP-501", + "teacher_id": "AK23", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-502", + "teacher_id": "AA04", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Wednesday": { + "F": [ + { + "subject_id": "XCS-501", + "teacher_id": "PA21", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "SP06", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-502", + "teacher_id": "SS03", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "JM12", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "PK02", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "TCS-509", + "teacher_id": "SJ16", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "TCS-503", + "teacher_id": "DP07", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-502", + "teacher_id": "SS03", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "SJ16", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "PK02", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "NJ13", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Thursday": { + "F": [ + { + "subject_id": "TCS-509", + "teacher_id": "SG19", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "TCS-502", + "teacher_id": "SS03", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "TCS-531", + "teacher_id": "AB01", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "TCS-531", + "teacher_id": "PK02", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "BJ10", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-502", + "teacher_id": "SS03", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "SG19", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Placement_Class", + "teacher_id": "AK26", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Placement_Class", + "teacher_id": "AK26", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Friday": { + "F": [ + { + "subject_id": "TMA-502", + "teacher_id": "NJ13", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Placement_Class", + "teacher_id": "AK26", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Placement_Class", + "teacher_id": "AK26", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "TCS-531", + "teacher_id": "AB01", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "AB17", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Saturday": { + "F": [ + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "16:50 - 17:50" + } + ] + } + }, + "L3": { + "Monday": { + "F": [ + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PMA-502", + "teacher_id": "PM14", + "time_slot": "14:50 - 15:50" + } + ] + }, + "Tuesday": { + "E": [ + { + "subject_id": "PMA-502", + "teacher_id": "AA15", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PMA-502", + "teacher_id": "AA15", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "PCS-506", + "teacher_id": "RD09", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-506", + "teacher_id": "RD09", + "time_slot": "14:50 - 15:50" + } + ], + "F": [ + { + "subject_id": "PCS-503", + "teacher_id": "VD25", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-503", + "teacher_id": "VD25", + "time_slot": "14:50 - 15:50" + } + ] + }, + "Wednesday": { + "D": [ + { + "subject_id": "PCS-503", + "teacher_id": "VD25", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-503", + "teacher_id": "VD25", + "time_slot": "14:50 - 15:50" + } + ], + "F": [ + { + "subject_id": "PCS-506", + "teacher_id": "RD09", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-506", + "teacher_id": "RD09", + "time_slot": "14:50 - 15:50" + } + ] + }, + "Thursday": { + "E": [ + { + "subject_id": "PCS-503", + "teacher_id": "DP07", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-503", + "teacher_id": "DP07", + "time_slot": "14:50 - 15:50" + } + ] + }, + "Friday": {}, + "Saturday": {} + }, + "R1": { + "Monday": { + "A": [ + { + "subject_id": "TCS-503", + "teacher_id": "SP06", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "SJ16", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "SCS-501", + "teacher_id": "AP24", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "CSP-501", + "teacher_id": "AK23", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Tuesday": {}, + "Wednesday": { + "A": [ + { + "subject_id": "TCS-531", + "teacher_id": "AB01", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "XCS-501", + "teacher_id": "DT20", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "SP06", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "SJ16", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "BJ10", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "XCS-501", + "teacher_id": "DT20", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Thursday": { + "A": [ + { + "subject_id": "SCS-501", + "teacher_id": "AP24", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "XCS-501", + "teacher_id": "DT20", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "BJ10", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "SJ16", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "SJ16", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "BJ10", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Friday": { + "A": [ + { + "subject_id": "TMA-502", + "teacher_id": "BJ10", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "AB01", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-503", + "teacher_id": "SP06", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "CSP-501", + "teacher_id": "AK23", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-502", + "teacher_id": "SS03", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-509", + "teacher_id": "SJ16", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TMA-502", + "teacher_id": "BJ10", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TCS-531", + "teacher_id": "AB01", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Saturday": { + "A": [ + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "teacher_id": "None", + "time_slot": "12:00 - 13:00" + } + ] + } + } +} \ No newline at end of file diff --git a/Constants/classroom_tt.py b/Constants/classroom_tt.py new file mode 100644 index 0000000..262c3fe --- /dev/null +++ b/Constants/classroom_tt.py @@ -0,0 +1,48 @@ +import json +from Samples.samples import (WorkingDays, SampleChromosome) + +class ClassroomTimetable: + def __init__(self): + # Dictionary to hold timetables for each classroom + self.classroom_timetable = {} + + def generate_classroom_timetable(self, chromosome): + for week, days in chromosome.items(): + for day, sections in days.items(): + for section, classes in sections.items(): + for entry in classes: + classroom_id = entry["classroom_id"] + + if classroom_id not in self.classroom_timetable: + self.classroom_timetable[classroom_id] = {day: {} for day in WorkingDays.days} + + if section not in self.classroom_timetable[classroom_id][day]: + self.classroom_timetable[classroom_id][day][section] = [] + + self.classroom_timetable[classroom_id][day][section].append({ + "subject_id": entry["subject_id"], + "teacher_id": entry["teacher_id"], + "time_slot": entry["time_slot"], + }) + + return self.classroom_timetable + + def save_timetable_to_json(self, file_path="Constants/classroom_timetable.json"): + try: + with open(file_path, "w") as json_file: + json.dump(self.classroom_timetable, json_file, indent=4) + # print(f"Timetable successfully saved to '{file_path}'.") + except Exception as e: + print(f"Error saving timetable to '{file_path}': {e}") + +if __name__ == "__main__": + # Generate classroom-wise timetable + classroom_timetable = ClassroomTimetable() + w = { + "Week 2": SampleChromosome.schedule2, + "Week 1": SampleChromosome.schedule1 + } + classroom_tt = classroom_timetable.generate_classroom_timetable(w) + print(classroom_tt) + # Save as JSON + classroom_timetable.save_timetable_to_json() diff --git a/Constants/classroom_tt_csv.py b/Constants/classroom_tt_csv.py new file mode 100644 index 0000000..6b59a1f --- /dev/null +++ b/Constants/classroom_tt_csv.py @@ -0,0 +1,83 @@ +import os +import csv +from Samples.samples import (SampleChromosome,WorkingDays) +from Constants.classroom_tt import ClassroomTimetable # Ensure correct import + +# Predefined time slots mapping +time_slots = { + 1: "08:00 - 09:00", + 2: "09:00 - 10:00", + 3: "11:00 - 12:00", + 4: "12:00 - 13:00", + 5: "16:50 - 17:50", +} + +# Reverse lookup to get slot number from time string +time_slot_order = {v: k for k, v in time_slots.items()} + +# List of days in the correct order +days_of_week_order =WorkingDays.days + +def extract_time_slots(timetable): + """Extract and sort time slots from timetable.""" + unique_slots = set() + + # Collect all unique time slots + for day_data in timetable.values(): + for slots in day_data.values(): + for entry in slots: + if isinstance(entry, dict) and "time_slot" in entry: # ✅ Ensure correct structure + unique_slots.add(entry["time_slot"]) + else: + print(f"⚠️ ERROR: Unexpected entry format -> {entry}") + + # Sort slots based on predefined order + sorted_slots = sorted(unique_slots, key=lambda slot: time_slot_order.get(slot, float('inf'))) + + return [("CLASS", slot) for slot in sorted_slots] # Only include class slots + +def classroom_json_to_csv(classroom_timetable, output_folder): + """Convert classroom timetable JSON to CSV.""" + os.makedirs(output_folder, exist_ok=True) + + for classroom_id, schedule in classroom_timetable.items(): + final_slots = extract_time_slots(schedule) + csv_file = os.path.join(output_folder, f"{classroom_id}.csv") + header = ["DAY"] + [slot[1] for slot in final_slots] + rows = [] + + # Sort days correctly + sorted_days = sorted(schedule.items(), key=lambda x: days_of_week_order.index(x[0])) + + for day, day_data in sorted_days: + row = [day] + ["" for _ in final_slots] # Empty slots initially + + for section, classes in day_data.items(): + for entry in classes: + for i, slot in enumerate(final_slots): + if entry["time_slot"] == slot[1]: + row[i + 1] = f"{entry['subject_id']} ({section}, {entry['teacher_id']})" + + rows.append(row) + + # Write to CSV + with open(csv_file, mode="w", newline="", encoding="utf-8") as file: + writer = csv.writer(file) + writer.writerow(header) + writer.writerows(rows) + +if __name__ == "__main__": + # Generate classroom-wise timetable + classroom_timetable = ClassroomTimetable() + weekly_schedule = { + "Week 2": SampleChromosome.schedule2, + "Week 1": SampleChromosome.schedule1 + } + + classroom_tt = classroom_timetable.generate_classroom_timetable(weekly_schedule) + + # Output folder for CSV files + output_folder = "output_csvs" + + # Convert the generated timetable to CSV + classroom_json_to_csv(classroom_tt, output_folder) diff --git a/Constants/constant.py b/Constants/constant.py index 0bf8d3f..4c4d0e2 100644 --- a/Constants/constant.py +++ b/Constants/constant.py @@ -1,28 +1,24 @@ class Defaults: - # All the defaults values over GA folder. - room_capacity = 60 - working_days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] - starting_section_fitness = 1000 - max_class_capacity = 250 - initial_no_of_chromosomes = 10 - total_no_of_generations = 10 + def __init__(self, config=None): + config = config or {} + self.room_capacity = config.get('room_capacity', 60) + self.working_days = config.get('working_days', ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]) + self.starting_section_fitness = config.get('starting_section_fitness', 1000) + self.max_class_capacity = config.get('max_class_capacity', 250) + self.initial_no_of_chromosomes = config.get('initial_no_of_chromosomes', 1000) + self.total_no_of_generations = config.get('total_no_of_generations', 100) + self.class_strength = config.get('class_strength', 50) -class Sections: - def __init__(self, section_number): - self.sections = [chr(65 + i) for i in range(section_number)] +class TeacherPreferences: + def __init__(self, teacher_weekly_workload: dict): + self.teacher_preferences = {} + for teacher, workload in teacher_weekly_workload.items(): + self.teacher_preferences[teacher] = list(range(1, 8)) -class Classrooms: - def __init__(self, room_count, lab_count): - self.classrooms = [f"R{i + 1}" for i in range(room_count)] - self.labs = [f"L{i + 1}" for i in range(lab_count)] - - -class RoomCapacity: - def __init__(self, classrooms, sections, default_capacity=Defaults.room_capacity): - self.room_capacity = {room: default_capacity for room in classrooms} - self.section_strength = {section: default_capacity for section in sections} + def get_preferences(self): + return self.teacher_preferences class SubjectWeeklyQuota: @@ -42,19 +38,23 @@ def __init__(self, teacher_duty_days: dict): class SpecialSubjects: - def __init__(self, special_subjects: list, labs: list, specialization_subjects: list): + def __init__( + self, special_subjects: list, labs: list, specialization_subjects: list + ): self.special_subjects = special_subjects self.labs = labs self.specialization_subjects = specialization_subjects class PenaltyConstants: - PENALTY_TEACHER_DOUBLE_BOOKED = 30 - PENALTY_CLASSROOM_DOUBLE_BOOKED = 20 - PENALTY_OVER_CAPACITY = 25 - PENALTY_UN_PREFERRED_SLOT = 5 - PENALTY_OVERLOAD_TEACHER = 10 - PENALTY_NON_DUTY_DAY=40 + def __init__(self, config=None): + config = config or {} + self.PENALTY_TEACHER_DOUBLE_BOOKED = config.get('teacher_double_booked', 30) + self.PENALTY_CLASSROOM_DOUBLE_BOOKED = config.get('classroom_double_booked', 20) + self.PENALTY_OVER_CAPACITY = config.get('over_capacity', 25) + self.PENALTY_UN_PREFERRED_SLOT = config.get('un_preferred_slot', 5) + self.PENALTY_OVERLOAD_TEACHER = config.get('overload_teacher', 10) + self.PENALTY_NON_DUTY_DAY = config.get('non_duty_day', 40) class SectionsConstants: @@ -67,3 +67,10 @@ class SectionsConstants: ATTRIBUTE_CONDITIONS = { 'hostler': lambda student: student.get('Hostler', False), } + + # Add class-level attributes to Defaults for backward compatibility + Defaults.working_days = Defaults().working_days + Defaults.max_class_capacity = Defaults().max_class_capacity + Defaults.initial_no_of_chromosomes = Defaults().initial_no_of_chromosomes + Defaults.total_no_of_generations = Defaults().total_no_of_generations + diff --git a/Constants/helper_routines.py b/Constants/helper_routines.py new file mode 100644 index 0000000..e5ac838 --- /dev/null +++ b/Constants/helper_routines.py @@ -0,0 +1,43 @@ +def update_teacher_availability_matrix(teacher_availability_matrix, best_chromosome): + for teacher_id, schedule_data in best_chromosome.items(): + if teacher_id in teacher_availability_matrix: + teacher_availability_matrix[teacher_id] = schedule_data + return teacher_availability_matrix + + +def update_matrix_for_best( + best_chromosome, teacher_availability_matrix, day_map, time_slot_map +): + for day_name, section_dict in best_chromosome.items(): + if day_name not in day_map: + continue # or raise an error + day_index = day_map[day_name] + + for section_name, schedule_items in section_dict.items(): + for item in schedule_items: + teacher_id = item["teacher_id"] + if teacher_id == "None": + continue + + time_slot_str = item["time_slot"] + if time_slot_str not in time_slot_map: + continue # or raise an error + slot_index = time_slot_map[time_slot_str] + + # Now set availability to False + teacher_availability_matrix[teacher_id][day_index][ + slot_index - 1 + ] = False + + return teacher_availability_matrix + + +def initialize_teacher_availability( + teacher_list, + num_days=5, + num_slots=7 +): + return { + teacher: [[True] * num_slots for _ in range(num_days)] + for teacher in teacher_list + } diff --git a/Constants/is_conflict.py b/Constants/is_conflict.py index d13e692..dfb98b3 100644 --- a/Constants/is_conflict.py +++ b/Constants/is_conflict.py @@ -1,7 +1,17 @@ import psycopg2 + from Samples.samples import SampleChromosome + + class IsConflict: - def __init__(self, dbname="timetable", user="postgres", password="root", host="localhost", port="5432"): + def __init__( + self, + dbname="timetable_postgres_db", + user="postgres", + password="13989333", + host="localhost", + port="5432", + ): self.dbname = dbname self.user = user self.password = password @@ -17,108 +27,112 @@ def connect_to_database(self): user=self.user, password=self.password, host=self.host, - port=self.port + port=self.port, ) self.cursor = self.conn.cursor() - print("Connected to PostgreSQL database!") except Exception as e: - print(f"Error connecting to the database: {e}") + print(f"Error connecting to database: {e}") raise def insert_schedule(self, timetable, chromosome_name): try: - for day, sections in timetable.items(): - for section, classes in sections.items(): - for cls in classes: - - print(f"Inserting data: {chromosome_name}, {day}, {section}, {cls['teacher_id']}, {cls['subject_id']}, {cls['classroom_id']}, {cls['time_slot']}") - self.cursor.execute(""" - INSERT INTO schedule (chromosome, day, section, teacher_id, subject_id, classroom_id, time_slot) - VALUES (%s, %s, %s, %s, %s, %s, %s); - """, (chromosome_name, day, section, cls['teacher_id'], cls['subject_id'], cls['classroom_id'], cls['time_slot'])) - self.conn.commit() - print(f"Schedule data for {chromosome_name} inserted successfully.") + for day, classes in timetable.items(): + for cls in classes: + self.cursor.execute( + """ + INSERT INTO schedule (chromosome, day, teacher_id, subject_id, classroom_id, time_slot) + VALUES (%s, %s, %s, %s, %s, %s); + """, + ( + chromosome_name, + day, + cls["teacher_id"], + cls["subject_id"], + "N/A", + cls["time_slot"], + ), + ) + self.conn.commit() except Exception as e: - self.conn.rollback() - print(f"Error inserting schedule data: {e}") + print(f"Error inserting schedule: {e}") + self.conn.rollback() def detect_teacher_conflicts(self): - self.cursor.execute(""" - SELECT s1.teacher_id, s1.day, s1.time_slot, COUNT(*) - FROM schedule s1 - JOIN schedule s2 - ON s1.teacher_id = s2.teacher_id - AND s1.time_slot = s2.time_slot - AND s1.day = s2.day - AND s1.chromosome != s2.chromosome - GROUP BY s1.teacher_id, s1.day, s1.time_slot - HAVING COUNT(*) > 1; - """) - return self.cursor.fetchall() - - def detect_classroom_conflicts(self): - self.cursor.execute(""" - SELECT s1.classroom_id, s1.day, s1.time_slot, COUNT(*) - FROM schedule s1 - JOIN schedule s2 - ON s1.classroom_id = s2.classroom_id - AND s1.time_slot = s2.time_slot - AND s1.day = s2.day - AND s1.chromosome != s2.chromosome - GROUP BY s1.classroom_id, s1.day, s1.time_slot - HAVING COUNT(*) > 1; - """) - return self.cursor.fetchall() + try: + self.cursor.execute( + """ + SELECT s1.teacher_id, s1.day, s1.time_slot + FROM schedule s1 + JOIN schedule s2 + ON s1.teacher_id = s2.teacher_id + AND s1.time_slot = s2.time_slot + AND s1.day = s2.day + AND ( + s1.chromosome != s2.chromosome -- Conflict across chromosomes + OR s1.chromosome = s2.chromosome -- Conflict within the same chromosome + ) + GROUP BY s1.teacher_id, s1.day, s1.time_slot + HAVING COUNT(s1.teacher_id) > 1; -- More than one class for the same teacher at the same time + """ + ) + return self.cursor.fetchall() + except Exception as e: + print(f"Error detecting teacher conflicts: {e}") + return [] def truncate_schedule(self): - self.cursor.execute("TRUNCATE TABLE schedule;") - self.conn.commit() - print("Schedule table truncated.") + try: + self.cursor.execute("TRUNCATE TABLE schedule;") + self.conn.commit() + except Exception as e: + print(f"Error truncating schedule: {e}") + self.conn.rollback() def close_connection(self): - if self.cursor: - self.cursor.close() - if self.conn: - self.conn.close() - print("\nDatabase connection closed.") + try: + if self.cursor: + self.cursor.close() + if self.conn: + self.conn.close() + except Exception as e: + print(f"Error closing connection: {e}") def process_schedules(self, timetable1, timetable2): try: self.connect_to_database() - # Insert Chromosome data for both timetables self.insert_schedule(timetable1, "Week 1") self.insert_schedule(timetable2, "Week 2") - # Check for Teacher Conflicts teacher_conflicts = self.detect_teacher_conflicts() + conflicts = [] - # Check for Classroom Conflicts - classroom_conflicts = self.detect_classroom_conflicts() - - # Return both conflicts as a dictionary - return { - "teacher_conflicts": teacher_conflicts, - "classroom_conflicts": classroom_conflicts - } + for conflict in teacher_conflicts: + conflicts.append( + { + "type": "Teacher Conflict", + "teacher": conflict[0], + "day": conflict[1], + "time_slot": conflict[2], + } + ) + return conflicts finally: - # Cleanup and close database connection self.truncate_schedule() self.close_connection() if __name__ == "__main__": timetable_processor = IsConflict() - sample_chromosome_1 = SampleChromosome() - sample_chromosome_2 = SampleChromosome() - chromosome1 = sample_chromosome_1.schedule1 - chromosome2 = sample_chromosome_2.schedule2 + sample_chromosome_1 = SampleChromosome() + sample_chromosome_2 = SampleChromosome() + chromosome1 = sample_chromosome_1.schedule1 + chromosome2 = sample_chromosome_2.schedule2 + + # Process the timetables and check for conflicts conflicts = timetable_processor.process_schedules(chromosome1, chromosome2) - print("\nTeacher Conflicts:") - for conflict in conflicts["teacher_conflicts"]: - print(f"Teacher ID: {conflict[0]}, Day: {conflict[1]}, Time Slot: {conflict[2]}, Conflict Count: {conflict[3]}") - print("\nClassroom Conflicts:") - for conflict in conflicts["classroom_conflicts"]: - print(f"Classroom ID: {conflict[0]}, Day: {conflict[1]}, Time Slot: {conflict[2]}, Conflict Count: {conflict[3]}") + # Print the detected conflicts (if any) + print("Conflicts detected:") + print(conflicts) diff --git a/Constants/is_conflict_new.py b/Constants/is_conflict_new.py new file mode 100644 index 0000000..0433014 --- /dev/null +++ b/Constants/is_conflict_new.py @@ -0,0 +1,154 @@ +import psycopg2 + +from Samples.samples import SampleChromosome + + +class IsConflict: + def __init__( + self, + dbname="timetable", + user="postgres", + password="root", + host="localhost", + port="5432", + ): + self.dbname = dbname + self.user = user + self.password = password + self.host = host + self.port = port + self.conn = None + self.cursor = None + + def connect_to_database(self): + try: + self.conn = psycopg2.connect( + dbname=self.dbname, + user=self.user, + password=self.password, + host=self.host, + port=self.port, + ) + self.cursor = self.conn.cursor() + print("Connected to PostgreSQL database!") + except Exception as e: + print(f"Error connecting to the database: {e}") + raise + + def insert_schedule(self, timetable, chromosome_name): + try: + for day, sections in timetable.items(): + for section, classes in sections.items(): + for cls in classes: + print( + f"Inserting data: {chromosome_name}, {day}, {section}, {cls['teacher_id']}, {cls['subject_id']}, {cls['classroom_id']}, {cls['time_slot']}" + ) + self.cursor.execute( + """ + INSERT INTO schedule (chromosome, day, section, teacher_id, subject_id, classroom_id, time_slot) + VALUES (%s, %s, %s, %s, %s, %s, %s); + """, + ( + chromosome_name, + day, + section, + cls["teacher_id"], + cls["subject_id"], + cls["classroom_id"], + cls["time_slot"], + ), + ) + self.conn.commit() + print(f"Schedule data for {chromosome_name} inserted successfully.") + except Exception as e: + self.conn.rollback() + print(f"Error inserting schedule data: {e}") + + def detect_teacher_conflicts(self): + self.cursor.execute( + """ + SELECT s1.teacher_id, s1.day, s1.time_slot, COUNT(*) + FROM schedule s1 + JOIN schedule s2 + ON s1.teacher_id = s2.teacher_id + AND s1.time_slot = s2.time_slot + AND s1.day = s2.day + AND s1.chromosome != s2.chromosome + GROUP BY s1.teacher_id, s1.day, s1.time_slot + HAVING COUNT(*) > 1; + """ + ) + return self.cursor.fetchall() + + def detect_classroom_conflicts(self): + self.cursor.execute( + """ + SELECT s1.classroom_id, s1.day, s1.time_slot, COUNT(*) + FROM schedule s1 + JOIN schedule s2 + ON s1.classroom_id = s2.classroom_id + AND s1.time_slot = s2.time_slot + AND s1.day = s2.day + AND s1.chromosome != s2.chromosome + GROUP BY s1.classroom_id, s1.day, s1.time_slot + HAVING COUNT(*) > 1; + """ + ) + return self.cursor.fetchall() + + def truncate_schedule(self): + self.cursor.execute("TRUNCATE TABLE schedule;") + self.conn.commit() + print("Schedule table truncated.") + + def close_connection(self): + if self.cursor: + self.cursor.close() + if self.conn: + self.conn.close() + print("\nDatabase connection closed.") + + def process_schedules(self, timetable1, timetable2): + try: + self.connect_to_database() + + # Insert Chromosome data for both timetables + self.insert_schedule(timetable1, "Week 1") + self.insert_schedule(timetable2, "Week 2") + + # Check for Teacher Conflicts + teacher_conflicts = self.detect_teacher_conflicts() + + # Check for Classroom Conflicts + classroom_conflicts = self.detect_classroom_conflicts() + + # Return both conflicts as a dictionary + return { + "teacher_conflicts": teacher_conflicts, + "classroom_conflicts": classroom_conflicts, + } + + finally: + # Cleanup and close database connection + self.truncate_schedule() + self.close_connection() + + +if __name__ == "__main__": + timetable_processor = IsConflict() + sample_chromosome_1 = SampleChromosome() + sample_chromosome_2 = SampleChromosome() + chromosome1 = sample_chromosome_1.schedule1 + chromosome2 = sample_chromosome_2.schedule2 + conflicts = timetable_processor.process_schedules(chromosome1, chromosome2) + print("\nTeacher Conflicts:") + for conflict in conflicts["teacher_conflicts"]: + print( + f"Teacher ID: {conflict[0]}, Day: {conflict[1]}, Time Slot: {conflict[2]}, Conflict Count: {conflict[3]}" + ) + + print("\nClassroom Conflicts:") + for conflict in conflicts["classroom_conflicts"]: + print( + f"Classroom ID: {conflict[0]}, Day: {conflict[1]}, Time Slot: {conflict[2]}, Conflict Count: {conflict[3]}" + ) diff --git a/Constants/section_allocation.py b/Constants/section_allocation.py new file mode 100644 index 0000000..8edf9c5 --- /dev/null +++ b/Constants/section_allocation.py @@ -0,0 +1,79 @@ +import random +from collections import defaultdict +from typing import Dict, List +from Constants.constant import SectionsConstants + + +class StudentScorer: + def __init__(self, attribute_weights: Dict[str, int] = None, config=None): + """ + Initialize the scorer with attribute weights. + Defaults to SectionsConstants.ATTRIBUTE_WEIGHTS if not provided. + """ + self.sections_constants = SectionsConstants(config) + self.attribute_weights = attribute_weights or self.sections_constants.ATTRIBUTE_WEIGHTS + + def calculate_dynamic_cgpa_threshold(self, students: List[Dict], top_percentage: int = 30) -> float: + """ + Calculate the CGPA threshold for the top X% of students. + """ + valid_cgpas = [student.get("cgpa", 0) for student in students if isinstance(student.get("cgpa"), (int, float))] + if not valid_cgpas: + return 0 # Default if no valid CGPAs + sorted_cgpas = sorted(valid_cgpas, reverse=True) + threshold_index = max(1, int(len(sorted_cgpas) * top_percentage / 100)) - 1 + return sorted_cgpas[threshold_index] + + def assign_dynamic_conditions(self, cgpa_threshold: float): + """ + Assign a dynamic condition for CGPA based on the threshold. + """ + self.sections_constants.ATTRIBUTE_CONDITIONS["good_cgpa"] = lambda student: student.get("cgpa", 0) >= cgpa_threshold + + def calculate_student_score(self, student: Dict) -> int: + """ + Calculate the score for a student based on attribute weights. + """ + total_score = 0 + for attribute, weight in self.attribute_weights.items(): + condition_func = self.sections_constants.ATTRIBUTE_CONDITIONS.get(attribute, lambda x: False) + if condition_func(student): + total_score += weight + return total_score + + def assign_scores_to_students(self, students): + # Step 1: Calculate CGPA threshold + cgpa_threshold = self.calculate_dynamic_cgpa_threshold(students) + self.assign_dynamic_conditions(cgpa_threshold) + + + # Step 2: Assign scores + for student in students: + student["score"] = self.calculate_student_score(student) + + + return students + + def divide_students_into_sections(self, students: List[Dict], class_strength: int = 50) -> List[List[Dict]]: + """ + Divide students into sections based on their scores and class strength. + """ + grouped_by_score = defaultdict(list) + for student in students: + grouped_by_score[student["score"]].append(student) + grouped_by_score[student["score"]].append(student) + + sections = [] + current_section = [] + + for score_group in grouped_by_score.values(): + for student in score_group: + current_section.append(student) + if len(current_section) == class_strength: + sections.append(current_section) + current_section = [] + + if current_section: + sections.append(current_section) + + return sections diff --git a/Constants/teacher_timetable.json b/Constants/teacher_timetable.json index 42c705c..e94b208 100644 --- a/Constants/teacher_timetable.json +++ b/Constants/teacher_timetable.json @@ -1,1050 +1,2720 @@ { - "AB01": { - "Monday": [ - { - "course": "Week 2", - "section": "B", - "subject_id": "TCS-531", - "time_slot": "2:15 - 3:10", - "classroom_id": "R2" - }, - { - "course": "Week 1", - "section": "D", - "subject_id": "TCS-531", - "time_slot": "1:20 - 2:15", - "classroom_id": "R4" - } - ], - "Tuesday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "TCS-531", - "time_slot": "9:00 - 9:55", - "classroom_id": "R1" - }, - { - "course": "Week 1", - "section": "B", - "subject_id": "TCS-531", - "time_slot": "9:55 - 10:50", - "classroom_id": "R2" - } - ], - "Wednesday": [ - { - "course": "Week 2", - "section": "C", - "subject_id": "TCS-531", - "time_slot": "11:10 - 12:05", - "classroom_id": "R3" - } - ], - "Thursday": [ - { - "course": "Week 2", - "section": "C", - "subject_id": "TCS-531", - "time_slot": "3:30 - 4:25", - "classroom_id": "R3" - } - ], - "Friday": [ - { - "course": "Week 2", - "section": "C", - "subject_id": "TCS-531", - "time_slot": "12:05 - 1:00", - "classroom_id": "R3" - } - ], - "Saturday": [] - }, - "PK02": { - "Monday": [], - "Tuesday": [ - { - "course": "Week 2", - "section": "C", - "subject_id": "TCS-531", - "time_slot": "12:05 - 1:00", - "classroom_id": "R3" - }, - { - "course": "Week 1", - "section": "C", - "subject_id": "TCS-531", - "time_slot": "1:20 - 2:15", - "classroom_id": "R3" - } - ], - "Wednesday": [ - { - "course": "Week 2", - "section": "D", - "subject_id": "TCS-531", - "time_slot": "2:15 - 3:10", - "classroom_id": "R4" - } - ], - "Thursday": [ - { - "course": "Week 1", - "section": "D", - "subject_id": "TCS-531", - "time_slot": "9:55 - 10:50", - "classroom_id": "R4" - } - ], - "Friday": [], - "Saturday": [] + "RS11": { + "Monday": { + "A": [ + { + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + } + ], + "D": [ + { + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Tuesday": { + "E": [ + { + "subject_id": "TMA-502", + "classroom_id": "R5", + "time_slot": "09:00 - 10:00" + } + ], + "D": [ + { + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "11:00 - 12:00" + } + ] + }, + "Wednesday": { + "E": [ + { + "subject_id": "TMA-502", + "classroom_id": "R5", + "time_slot": "08:00 - 09:00" + } + ], + "C": [ + { + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Thursday": { + "B": [ + { + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "08:00 - 09:00" + } + ], + "F": [ + { + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + } + ], + "A": [ + { + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Friday": { + "C": [ + { + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "11:00 - 12:00" + } + ], + "B": [ + { + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "11:00 - 12:00" + } + ] + }, + "Saturday": {} }, - "SS03": { - "Monday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "TCS-502", - "time_slot": "3:30 - 4:25", - "classroom_id": "R1" - } - ], - "Tuesday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "TCS-502", - "time_slot": "12:05 - 1:00", - "classroom_id": "R1" - }, - { - "course": "Week 1", - "section": "B", - "subject_id": "TCS-502", - "time_slot": "9:00 - 9:55", - "classroom_id": "R2" - } - ], - "Wednesday": [ - { - "course": "Week 2", - "section": "B", - "subject_id": "TCS-502", - "time_slot": "9:55 - 10:50", - "classroom_id": "R2" - } - ], - "Thursday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "TCS-502", - "time_slot": "2:15 - 3:10", - "classroom_id": "R1" - } - ], - "Friday": [], - "Saturday": [] + "DT20": { + "Monday": { + "B": [ + { + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "08:00 - 09:00" + } + ], + "E": [ + { + "subject_id": "XCS-501", + "classroom_id": "R5", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "XCS-501", + "classroom_id": "R5", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Tuesday": { + "B": [ + { + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "09:00 - 10:00" + } + ], + "F": [ + { + "subject_id": "XCS-501", + "classroom_id": "R6", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Wednesday": { + "A": [ + { + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Thursday": { + "A": [ + { + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Friday": { + "B": [ + { + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Saturday": {} }, - "AA04": { - "Monday": [], - "Tuesday": [ - { - "course": "Week 2", - "section": "C", - "subject_id": "TCS-502", - "time_slot": "3:30 - 4:25", - "classroom_id": "R3" - } - ], - "Wednesday": [], - "Thursday": [ - { - "course": "Week 2", - "section": "B", - "subject_id": "TCS-502", - "time_slot": "9:00 - 9:55", - "classroom_id": "R2" - } - ], - "Friday": [ - { - "course": "Week 2", - "section": "D", - "subject_id": "TCS-502", - "time_slot": "12:05 - 1:00", - "classroom_id": "R4" - } - ], - "Saturday": [] + "AB01": { + "Monday": { + "B": [ + { + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Tuesday": { + "B": [ + { + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "08:00 - 09:00" + } + ], + "E": [ + { + "subject_id": "TCS-531", + "classroom_id": "R5", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-531", + "classroom_id": "R5", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Wednesday": { + "A": [ + { + "subject_id": "TCS-531", + "classroom_id": "R1", + "time_slot": "08:00 - 09:00" + } + ], + "E": [ + { + "subject_id": "TCS-531", + "classroom_id": "R5", + "time_slot": "09:00 - 10:00" + } + ], + "B": [ + { + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "09:00 - 10:00" + } + ], + "D": [ + { + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Thursday": { + "B": [ + { + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "12:00 - 13:00" + } + ], + "F": [ + { + "subject_id": "TCS-531", + "classroom_id": "R6", + "time_slot": "16:50 - 17:50" + } + ], + "E": [ + { + "subject_id": "TCS-531", + "classroom_id": "R5", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Friday": { + "A": [ + { + "subject_id": "TCS-531", + "classroom_id": "R1", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-531", + "classroom_id": "R1", + "time_slot": "12:00 - 13:00" + } + ], + "D": [ + { + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "09:00 - 10:00" + } + ], + "F": [ + { + "subject_id": "TCS-531", + "classroom_id": "R6", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Saturday": {} }, - "AC05": { - "Monday": [], - "Tuesday": [ - { - "course": "Week 2", - "section": "C", - "subject_id": "TCS-503", - "time_slot": "9:55 - 10:50", - "classroom_id": "R3" - }, - { - "course": "Week 2", - "section": "D", - "subject_id": "TCS-502", - "time_slot": "12:05 - 1:00", - "classroom_id": "R4" - } - ], - "Wednesday": [ - { - "course": "Week 1", - "section": "D", - "subject_id": "TCS-503", - "time_slot": "11:10 - 12:05", - "classroom_id": "R4" - } - ], - "Thursday": [ - { - "course": "Week 2", - "section": "C", - "subject_id": "TCS-503", - "time_slot": "1:20 - 2:15", - "classroom_id": "R3" - } - ], - "Friday": [ - { - "course": "Week 2", - "section": "C", - "subject_id": "TCS-503", - "time_slot": "9:00 - 9:55", - "classroom_id": "R3" - } - ], - "Saturday": [] + "AP24": { + "Monday": { + "B": [ + { + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "09:00 - 10:00" + } + ], + "C": [ + { + "subject_id": "SCS-501", + "classroom_id": "R3", + "time_slot": "11:00 - 12:00" + } + ], + "E": [ + { + "subject_id": "SCS-501", + "classroom_id": "R5", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "SCS-501", + "classroom_id": "R5", + "time_slot": "11:00 - 12:00" + } + ], + "F": [ + { + "subject_id": "SCS-501", + "classroom_id": "R6", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "SCS-501", + "classroom_id": "R6", + "time_slot": "08:00 - 09:00" + } + ], + "A": [ + { + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "11:00 - 12:00" + } + ], + "D": [ + { + "subject_id": "SCS-501", + "classroom_id": "R4", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Tuesday": { + "D": [ + { + "subject_id": "SCS-501", + "classroom_id": "R4", + "time_slot": "11:00 - 12:00" + } + ], + "C": [ + { + "subject_id": "SCS-501", + "classroom_id": "R3", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Wednesday": {}, + "Thursday": { + "A": [ + { + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Friday": {}, + "Saturday": {} }, - "SP06": { - "Monday": [ - { - "course": "Week 2", - "section": "B", - "subject_id": "TCS-503", - "time_slot": "1:20 - 2:15", - "classroom_id": "R2" - } - ], - "Tuesday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "TCS-503", - "time_slot": "11:10 - 12:05", - "classroom_id": "R1" - }, - { - "course": "Week 1", - "section": "C", - "subject_id": "TCS-503", - "time_slot": "2:15 - 3:10", - "classroom_id": "R3" - } - ], - "Wednesday": [ - { - "course": "Week 2", - "section": "D", - "subject_id": "PCS-503", - "time_slot": "9:55 - 10:50", - "classroom_id": "L4" - }, - { - "course": "Week 2", - "section": "D", - "subject_id": "PCS-503", - "time_slot": "9:00 - 9:55", - "classroom_id": "L4" - } - ], - "Thursday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "TCS-503", - "time_slot": "3:30 - 4:25", - "classroom_id": "R1" - } - ], - "Friday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "TCS-503", - "time_slot": "12:05 - 1:00", - "classroom_id": "R1" - } - ], - "Saturday": [] + "BJ10": { + "Monday": { + "B": [ + { + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Tuesday": { + "D": [ + { + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "13:50 - 14:50" + } + ], + "C": [ + { + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "11:00 - 12:00" + } + ] + }, + "Wednesday": { + "D": [ + { + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "16:50 - 17:50" + } + ], + "A": [ + { + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Thursday": { + "A": [ + { + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "09:00 - 10:00" + } + ], + "F": [ + { + "subject_id": "TMA-502", + "classroom_id": "R6", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Friday": { + "A": [ + { + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "11:00 - 12:00" + } + ] + }, + "Saturday": {} }, - "DP07": { - "Monday": [ - { - "course": "Week 2", - "section": "C", - "subject_id": "PCS-503", - "time_slot": "12:05 - 1:00", - "classroom_id": "L4" - }, - { - "course": "Week 2", - "section": "C", - "subject_id": "PCS-503", - "time_slot": "11:10 - 12:05", - "classroom_id": "L4" - }, - { - "course": "Week 1", - "section": "C", - "subject_id": "TCS-503", - "time_slot": "9:00 - 9:55", - "classroom_id": "R3" - } - ], - "Tuesday": [], - "Wednesday": [ - { - "course": "Week 2", - "section": "C", - "subject_id": "PCS-503", - "time_slot": "2:15 - 3:10", - "classroom_id": "L1" - }, - { - "course": "Week 2", - "section": "C", - "subject_id": "PCS-503", - "time_slot": "1:20 - 2:15", - "classroom_id": "L1" - } - ], - "Thursday": [ - { - "course": "Week 2", - "section": "B", - "subject_id": "TCS-503", - "time_slot": "9:55 - 10:50", - "classroom_id": "R2" - } - ], - "Friday": [], - "Saturday": [] + "AK23": { + "Monday": { + "C": [ + { + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "09:00 - 10:00" + } + ], + "D": [ + { + "subject_id": "CSP-501", + "classroom_id": "R4", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "CSP-501", + "classroom_id": "R4", + "time_slot": "12:00 - 13:00" + } + ], + "E": [ + { + "subject_id": "CSP-501", + "classroom_id": "R5", + "time_slot": "11:00 - 12:00" + } + ], + "F": [ + { + "subject_id": "CSP-501", + "classroom_id": "R6", + "time_slot": "08:00 - 09:00" + } + ], + "A": [ + { + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "12:00 - 13:00" + } + ], + "B": [ + { + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "11:00 - 12:00" + } + ] + }, + "Tuesday": { + "E": [ + { + "subject_id": "CSP-501", + "classroom_id": "R5", + "time_slot": "08:00 - 09:00" + } + ], + "F": [ + { + "subject_id": "CSP-501", + "classroom_id": "R6", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Wednesday": { + "B": [ + { + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Thursday": {}, + "Friday": { + "A": [ + { + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Saturday": {} }, - "AD08": { - "Monday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "PCS-506", - "time_slot": "12:05 - 1:00", - "classroom_id": "L5" - }, - { - "course": "Week 2", - "section": "A", - "subject_id": "PCS-506", - "time_slot": "11:10 - 12:05", - "classroom_id": "L5" - } - ], - "Tuesday": [ - { - "course": "Week 2", - "section": "D", - "subject_id": "PCS-506", - "time_slot": "9:55 - 10:50", - "classroom_id": "L3" - }, - { - "course": "Week 2", - "section": "D", - "subject_id": "PCS-506", - "time_slot": "9:00 - 9:55", - "classroom_id": "L3" - } - ], - "Wednesday": [], - "Thursday": [], - "Friday": [ - { - "course": "Week 2", - "section": "C", - "subject_id": "PCS-506", - "time_slot": "2:15 - 3:10", - "classroom_id": "L5" - }, - { - "course": "Week 2", - "section": "C", - "subject_id": "PCS-506", - "time_slot": "1:20 - 2:15", - "classroom_id": "L5" - } - ], - "Saturday": [] + "SS03": { + "Monday": { + "C": [ + { + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "09:00 - 10:00" + } + ], + "D": [ + { + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Tuesday": { + "B": [ + { + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "12:00 - 13:00" + } + ], + "D": [ + { + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "14:50 - 15:50" + } + ], + "E": [ + { + "subject_id": "TCS-502", + "classroom_id": "R5", + "time_slot": "11:00 - 12:00" + } + ] + }, + "Wednesday": { + "B": [ + { + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "11:00 - 12:00" + } + ], + "F": [ + { + "subject_id": "TCS-502", + "classroom_id": "R6", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "TCS-502", + "classroom_id": "R6", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Thursday": { + "D": [ + { + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "08:00 - 09:00" + } + ], + "F": [ + { + "subject_id": "TCS-502", + "classroom_id": "R6", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "TCS-502", + "classroom_id": "R6", + "time_slot": "11:00 - 12:00" + } + ], + "C": [ + { + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Friday": { + "D": [ + { + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "08:00 - 09:00" + } + ], + "A": [ + { + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Saturday": {} }, - "RD09": { - "Monday": [ - { - "course": "Week 2", - "section": "C", - "subject_id": "PCS-506", - "time_slot": "9:55 - 10:50", - "classroom_id": "L5" - }, - { - "course": "Week 2", - "section": "C", - "subject_id": "PCS-506", - "time_slot": "9:00 - 9:55", - "classroom_id": "L5" - } - ], - "Tuesday": [], - "Wednesday": [], - "Thursday": [], - "Friday": [], - "Saturday": [] + "PA21": { + "Monday": { + "C": [ + { + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "12:00 - 13:00" + } + ], + "F": [ + { + "subject_id": "XCS-501", + "classroom_id": "R6", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "XCS-501", + "classroom_id": "R6", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Tuesday": { + "D": [ + { + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Wednesday": { + "F": [ + { + "subject_id": "XCS-501", + "classroom_id": "R6", + "time_slot": "08:00 - 09:00" + } + ], + "D": [ + { + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Thursday": {}, + "Friday": {}, + "Saturday": {} }, - "BJ10": { - "Monday": [ - { - "course": "Week 1", - "section": "A", - "subject_id": "TMA-502", - "time_slot": "1:20 - 2:15", - "classroom_id": "R1" - } - ], - "Tuesday": [ - { - "course": "Week 2", - "section": "C", - "subject_id": "TMA-502", - "time_slot": "2:15 - 3:10", - "classroom_id": "R3" - } - ], - "Wednesday": [ - { - "course": "Week 2", - "section": "B", - "subject_id": "TMA-502", - "time_slot": "12:05 - 1:00", - "classroom_id": "R2" - } - ], - "Thursday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "TMA-502", - "time_slot": "11:10 - 12:05", - "classroom_id": "R1" - } - ], - "Friday": [ - { - "course": "Week 1", - "section": "B", - "subject_id": "TMA-502", - "time_slot": "9:00 - 9:55", - "classroom_id": "R2" - } - ], - "Saturday": [] + "SP06": { + "Monday": { + "D": [ + { + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "09:00 - 10:00" + } + ], + "A": [ + { + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "08:00 - 09:00" + } + ], + "E": [ + { + "subject_id": "TCS-503", + "classroom_id": "R5", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Tuesday": { + "B": [ + { + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "11:00 - 12:00" + } + ], + "F": [ + { + "subject_id": "TCS-503", + "classroom_id": "R6", + "time_slot": "16:50 - 17:50" + } + ], + "E": [ + { + "subject_id": "TCS-503", + "classroom_id": "R5", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Wednesday": { + "A": [ + { + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "11:00 - 12:00" + } + ], + "F": [ + { + "subject_id": "TCS-503", + "classroom_id": "R6", + "time_slot": "09:00 - 10:00" + } + ], + "B": [ + { + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "08:00 - 09:00" + } + ], + "E": [ + { + "subject_id": "TCS-503", + "classroom_id": "R5", + "time_slot": "14:50 - 15:50" + } + ] + }, + "Thursday": { + "B": [ + { + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "11:00 - 12:00" + } + ], + "C": [ + { + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Friday": { + "A": [ + { + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "11:00 - 12:00" + } + ], + "B": [ + { + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Saturday": {} }, - "RS11": { - "Monday": [ - { - "course": "Week 2", - "section": "B", - "subject_id": "PCS-503", - "time_slot": "9:55 - 10:50", - "classroom_id": "L3" - }, - { - "course": "Week 2", - "section": "B", - "subject_id": "PCS-503", - "time_slot": "9:00 - 9:55", - "classroom_id": "L3" - } - ], - "Tuesday": [], - "Wednesday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "PCS-503", - "time_slot": "12:05 - 1:00", - "classroom_id": "L3" - }, - { - "course": "Week 2", - "section": "A", - "subject_id": "PCS-503", - "time_slot": "11:10 - 12:05", - "classroom_id": "L3" - } - ], - "Thursday": [ - { - "course": "Week 1", - "section": "B", - "subject_id": "TMA-502", - "time_slot": "1:20 - 2:15", - "classroom_id": "R2" - } - ], - "Friday": [ - { - "course": "Week 1", - "section": "B", - "subject_id": "PCS-503", - "time_slot": "2:15 - 3:10", - "classroom_id": "L3" - } - ], - "Saturday": [] + "AD08": { + "Monday": { + "D": [ + { + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "14:50 - 15:50" + } + ], + "C": [ + { + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Tuesday": { + "C": [ + { + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + } + ], + "A": [ + { + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Wednesday": { + "C": [ + { + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + } + ], + "E": [ + { + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Thursday": { + "E": [ + { + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "14:50 - 15:50" + } + ], + "D": [ + { + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Friday": { + "B": [ + { + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + } + ], + "D": [ + { + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Saturday": {} }, - "JM12": { - "Monday": [], - "Tuesday": [], - "Wednesday": [ - { - "course": "Week 1", - "section": "D", - "subject_id": "TMA-502", - "time_slot": "12:05 - 1:00", - "classroom_id": "R4" - } - ], - "Thursday": [ - { - "course": "Week 2", - "section": "C", - "subject_id": "TMA-502", - "time_slot": "9:55 - 10:50", - "classroom_id": "R3" - }, - { - "course": "Week 1", - "section": "D", - "subject_id": "TMA-502", - "time_slot": "11:10 - 12:05", - "classroom_id": "R4" - } - ], - "Friday": [], - "Saturday": [] + "NB22": { + "Monday": { + "D": [ + { + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Tuesday": { + "E": [ + { + "subject_id": "XCS-501", + "classroom_id": "R5", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "XCS-501", + "classroom_id": "R5", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Wednesday": {}, + "Thursday": {}, + "Friday": {}, + "Saturday": {} }, - "NJ13": { - "Monday": [], - "Tuesday": [], - "Wednesday": [], - "Thursday": [ - { - "course": "Week 2", - "section": "D", - "subject_id": "TMA-502", - "time_slot": "9:00 - 9:55", - "classroom_id": "R4" - } - ], - "Friday": [], - "Saturday": [] + "PK02": { + "Monday": { + "E": [ + { + "subject_id": "TCS-531", + "classroom_id": "R5", + "time_slot": "08:00 - 09:00" + } + ], + "D": [ + { + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "13:50 - 14:50" + } + ] + }, + "Tuesday": { + "D": [ + { + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "09:00 - 10:00" + } + ], + "F": [ + { + "subject_id": "TCS-531", + "classroom_id": "R6", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Wednesday": { + "C": [ + { + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "11:00 - 12:00" + } + ], + "F": [ + { + "subject_id": "TCS-531", + "classroom_id": "R6", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "TCS-531", + "classroom_id": "R6", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Thursday": { + "D": [ + { + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "09:00 - 10:00" + } + ], + "F": [ + { + "subject_id": "TCS-531", + "classroom_id": "R6", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Friday": { + "C": [ + { + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "12:00 - 13:00" + } + ], + "E": [ + { + "subject_id": "TCS-531", + "classroom_id": "R5", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Saturday": {} }, - "PM14": { - "Monday": [ - { - "course": "Week 2", - "section": "D", - "subject_id": "PMA-502", - "time_slot": "9:55 - 10:50", - "classroom_id": "L1" - }, - { - "course": "Week 2", - "section": "D", - "subject_id": "PMA-502", - "time_slot": "9:00 - 9:55", - "classroom_id": "L1" - }, - { - "course": "Week 1", - "section": "B", - "subject_id": "PMA-502", - "time_slot": "12:05 - 1:00", - "classroom_id": "L4" - }, - { - "course": "Week 1", - "section": "B", - "subject_id": "PMA-502", - "time_slot": "11:10 - 12:05", - "classroom_id": "L4" - } - ], - "Tuesday": [], - "Wednesday": [], - "Thursday": [], - "Friday": [ - { - "course": "Week 1", - "section": "C", - "subject_id": "PMA-502", - "time_slot": "2:15 - 3:10", - "classroom_id": "L5" - }, - { - "course": "Week 1", - "section": "C", - "subject_id": "PMA-502", - "time_slot": "1:20 - 2:15", - "classroom_id": "L5" - } - ], - "Saturday": [] + "AA04": { + "Monday": { + "E": [ + { + "subject_id": "TCS-502", + "classroom_id": "R5", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Tuesday": { + "C": [ + { + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "08:00 - 09:00" + } + ], + "E": [ + { + "subject_id": "TCS-502", + "classroom_id": "R5", + "time_slot": "08:00 - 09:00" + } + ], + "F": [ + { + "subject_id": "TCS-502", + "classroom_id": "R6", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Wednesday": { + "C": [ + { + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "09:00 - 10:00" + } + ], + "E": [ + { + "subject_id": "TCS-502", + "classroom_id": "R5", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Thursday": { + "E": [ + { + "subject_id": "TCS-502", + "classroom_id": "R5", + "time_slot": "09:00 - 10:00" + } + ], + "D": [ + { + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Friday": { + "B": [ + { + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Saturday": {} }, - "AA15": { - "Monday": [], - "Tuesday": [], - "Wednesday": [], - "Thursday": [], - "Friday": [], - "Saturday": [] + "DP07": { + "Monday": { + "E": [ + { + "subject_id": "TCS-503", + "classroom_id": "R5", + "time_slot": "13:50 - 14:50" + } + ], + "C": [ + { + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "08:00 - 09:00" + } + ], + "F": [ + { + "subject_id": "TCS-503", + "classroom_id": "R6", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Tuesday": { + "C": [ + { + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "09:00 - 10:00" + } + ], + "D": [ + { + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "14:50 - 15:50" + } + ], + "F": [ + { + "subject_id": "TCS-503", + "classroom_id": "R6", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Wednesday": { + "B": [ + { + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "09:00 - 10:00" + } + ], + "C": [ + { + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "08:00 - 09:00" + } + ], + "F": [ + { + "subject_id": "TCS-503", + "classroom_id": "R6", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Thursday": { + "C": [ + { + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + } + ], + "E": [ + { + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "14:50 - 15:50" + } + ] + }, + "Friday": { + "C": [ + { + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Saturday": {} }, "SJ16": { - "Monday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "TCS-509", - "time_slot": "9:55 - 10:50", - "classroom_id": "R1" - }, - { - "course": "Week 1", - "section": "A", - "subject_id": "TCS-509", - "time_slot": "2:15 - 3:10", - "classroom_id": "R1" - } - ], - "Tuesday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "TCS-509", - "time_slot": "3:30 - 4:25", - "classroom_id": "R1" - } - ], - "Wednesday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "TCS-509", - "time_slot": "9:00 - 9:55", - "classroom_id": "R1" - } - ], - "Thursday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "TCS-509", - "time_slot": "12:05 - 1:00", - "classroom_id": "R1" - } - ], - "Friday": [], - "Saturday": [] + "Monday": { + "E": [ + { + "subject_id": "TCS-509", + "classroom_id": "R5", + "time_slot": "14:50 - 15:50" + } + ], + "A": [ + { + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Tuesday": { + "D": [ + { + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "09:00 - 10:00" + } + ], + "C": [ + { + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Wednesday": { + "A": [ + { + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "12:00 - 13:00" + } + ], + "F": [ + { + "subject_id": "TCS-509", + "classroom_id": "R6", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "TCS-509", + "classroom_id": "R6", + "time_slot": "11:00 - 12:00" + } + ], + "B": [ + { + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Thursday": { + "A": [ + { + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Friday": { + "C": [ + { + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "08:00 - 09:00" + } + ], + "A": [ + { + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Saturday": {} }, "AB17": { - "Monday": [ - { - "course": "Week 2", - "section": "B", - "subject_id": "TCS-509", - "time_slot": "12:05 - 1:00", - "classroom_id": "R2" - }, - { - "course": "Week 1", - "section": "B", - "subject_id": "TCS-509", - "time_slot": "9:55 - 10:50", - "classroom_id": "R2" - } - ], - "Tuesday": [], - "Wednesday": [ - { - "course": "Week 2", - "section": "B", - "subject_id": "TCS-509", - "time_slot": "11:10 - 12:05", - "classroom_id": "R2" - }, - { - "course": "Week 1", - "section": "B", - "subject_id": "TCS-509", - "time_slot": "9:00 - 9:55", - "classroom_id": "R2" - } - ], - "Thursday": [], - "Friday": [ - { - "course": "Week 1", - "section": "C", - "subject_id": "TCS-509", - "time_slot": "3:30 - 4:25", - "classroom_id": "R3" - } - ], - "Saturday": [] + "Monday": { + "F": [ + { + "subject_id": "TCS-509", + "classroom_id": "R6", + "time_slot": "09:00 - 10:00" + } + ], + "D": [ + { + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "11:00 - 12:00" + } + ] + }, + "Tuesday": { + "E": [ + { + "subject_id": "TCS-509", + "classroom_id": "R5", + "time_slot": "16:50 - 17:50" + } + ], + "D": [ + { + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Wednesday": { + "B": [ + { + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "11:00 - 12:00" + } + ], + "C": [ + { + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Thursday": { + "B": [ + { + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "09:00 - 10:00" + } + ], + "C": [ + { + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "11:00 - 12:00" + } + ] + }, + "Friday": { + "F": [ + { + "subject_id": "TCS-509", + "classroom_id": "R6", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Saturday": {} }, - "HP18": { - "Monday": [ - { - "course": "Week 1", - "section": "C", - "subject_id": "TCS-509", - "time_slot": "9:55 - 10:50", - "classroom_id": "R3" - } - ], - "Tuesday": [], - "Wednesday": [ - { - "course": "Week 2", - "section": "D", - "subject_id": "TCS-509", - "time_slot": "3:30 - 4:25", - "classroom_id": "R4" - } - ], - "Thursday": [], - "Friday": [ - { - "course": "Week 2", - "section": "D", - "subject_id": "TCS-509", - "time_slot": "11:10 - 12:05", - "classroom_id": "R4" - }, - { - "course": "Week 1", - "section": "D", - "subject_id": "TCS-509", - "time_slot": "12:05 - 1:00", - "classroom_id": "R4" - } - ], - "Saturday": [] + "PM14": { + "Monday": { + "F": [ + { + "subject_id": "PMA-502", + "classroom_id": "L3", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L3", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "14:50 - 15:50" + } + ] + }, + "Tuesday": { + "A": [ + { + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Wednesday": { + "D": [ + { + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "14:50 - 15:50" + } + ], + "A": [ + { + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Thursday": { + "B": [ + { + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Friday": {}, + "Saturday": {} }, - "SG19": { - "Monday": [ - { - "course": "Week 1", - "section": "D", - "subject_id": "TCS-509", - "time_slot": "2:15 - 3:10", - "classroom_id": "R4" - } - ], - "Tuesday": [ - { - "course": "Week 1", - "section": "D", - "subject_id": "TCS-509", - "time_slot": "9:00 - 9:55", - "classroom_id": "R4" - } - ], - "Wednesday": [], - "Thursday": [], - "Friday": [], - "Saturday": [] + "AC05": { + "Monday": { + "F": [ + { + "subject_id": "TCS-503", + "classroom_id": "R6", + "time_slot": "16:50 - 17:50" + } + ], + "D": [ + { + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "14:50 - 15:50" + } + ] + }, + "Tuesday": { + "D": [ + { + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "13:50 - 14:50" + } + ], + "F": [ + { + "subject_id": "TCS-502", + "classroom_id": "R6", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Wednesday": { + "D": [ + { + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Thursday": { + "E": [ + { + "subject_id": "TCS-503", + "classroom_id": "R5", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-502", + "classroom_id": "R5", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Friday": { + "E": [ + { + "subject_id": "TCS-503", + "classroom_id": "R5", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Saturday": {} }, - "DT20": { - "Monday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "XCS-501", - "time_slot": "1:20 - 2:15", - "classroom_id": "R1" - } - ], - "Tuesday": [ - { - "course": "Week 2", - "section": "D", - "subject_id": "XCS-501", - "time_slot": "11:10 - 12:05", - "classroom_id": "R4" - }, - { - "course": "Week 1", - "section": "D", - "subject_id": "XCS-501", - "time_slot": "12:05 - 1:00", - "classroom_id": "R4" - } - ], - "Wednesday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "XCS-501", - "time_slot": "9:55 - 10:50", - "classroom_id": "R1" - } - ], - "Thursday": [ - { - "course": "Week 2", - "section": "C", - "subject_id": "XCS-501", - "time_slot": "9:00 - 9:55", - "classroom_id": "R3" - } - ], - "Friday": [ - { - "course": "Week 2", - "section": "C", - "subject_id": "XCS-501", - "time_slot": "3:30 - 4:25", - "classroom_id": "R3" - } - ], - "Saturday": [] + "AA15": { + "Monday": {}, + "Tuesday": { + "E": [ + { + "subject_id": "PMA-502", + "classroom_id": "L3", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PMA-502", + "classroom_id": "L3", + "time_slot": "14:50 - 15:50" + } + ] + }, + "Wednesday": {}, + "Thursday": {}, + "Friday": {}, + "Saturday": {} }, - "PA21": { - "Monday": [ - { - "course": "Week 2", - "section": "B", - "subject_id": "XCS-501", - "time_slot": "3:30 - 4:25", - "classroom_id": "R2" - } - ], - "Tuesday": [ - { - "course": "Week 2", - "section": "B", - "subject_id": "XCS-501", - "time_slot": "9:55 - 10:50", - "classroom_id": "R2" - } - ], - "Wednesday": [ - { - "course": "Week 2", - "section": "C", - "subject_id": "XCS-501", - "time_slot": "12:05 - 1:00", - "classroom_id": "R3" - } - ], - "Thursday": [ - { - "course": "Week 2", - "section": "D", - "subject_id": "XCS-501", - "time_slot": "11:10 - 12:05", - "classroom_id": "R4" - } - ], - "Friday": [], - "Saturday": [] + "RD09": { + "Monday": {}, + "Tuesday": { + "F": [ + { + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "14:50 - 15:50" + } + ], + "E": [ + { + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "14:50 - 15:50" + } + ] + }, + "Wednesday": { + "F": [ + { + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "14:50 - 15:50" + } + ] + }, + "Thursday": {}, + "Friday": {}, + "Saturday": {} }, - "NB22": { - "Monday": [ - { - "course": "Week 2", - "section": "D", - "subject_id": "XCS-501", - "time_slot": "11:10 - 12:05", - "classroom_id": "R4" - } - ], - "Tuesday": [ - { - "course": "Week 2", - "section": "C", - "subject_id": "XCS-501", - "time_slot": "9:00 - 9:55", - "classroom_id": "R3" - }, - { - "course": "Week 1", - "section": "C", - "subject_id": "XCS-501", - "time_slot": "9:55 - 10:50", - "classroom_id": "R3" - } - ], - "Wednesday": [ - { - "course": "Week 2", - "section": "D", - "subject_id": "XCS-501", - "time_slot": "12:05 - 1:00", - "classroom_id": "R4" - } - ], - "Thursday": [], - "Friday": [], - "Saturday": [] + "HP18": { + "Monday": { + "E": [ + { + "subject_id": "TCS-509", + "classroom_id": "R5", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Tuesday": {}, + "Wednesday": { + "D": [ + { + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "11:00 - 12:00" + } + ] + }, + "Thursday": { + "D": [ + { + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "16:50 - 17:50" + } + ], + "E": [ + { + "subject_id": "TCS-509", + "classroom_id": "R5", + "time_slot": "11:00 - 12:00" + } + ] + }, + "Friday": {}, + "Saturday": {} }, - "AK23": { - "Monday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "CSP-501", - "time_slot": "9:00 - 9:55", - "classroom_id": "R1" - }, - { - "course": "Week 1", - "section": "D", - "subject_id": "CSP-501", - "time_slot": "3:30 - 4:25", - "classroom_id": "R4" - } - ], - "Tuesday": [ - { - "course": "Week 2", - "section": "C", - "subject_id": "CSP-501", - "time_slot": "11:10 - 12:05", - "classroom_id": "R3" - } - ], - "Wednesday": [], - "Thursday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "CSP-501", - "time_slot": "1:20 - 2:15", - "classroom_id": "R1" - }, - { - "course": "Week 2", - "section": "C", - "subject_id": "CSP-501", - "time_slot": "2:15 - 3:10", - "classroom_id": "R3" - }, - { - "course": "Week 2", - "section": "D", - "subject_id": "CSP-501", - "time_slot": "12:05 - 1:00", - "classroom_id": "R4" - } - ], - "Friday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "CSP-501", - "time_slot": "9:55 - 10:50", - "classroom_id": "R1" - } - ], - "Saturday": [] + "VD25": { + "Monday": {}, + "Tuesday": { + "F": [ + { + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "14:50 - 15:50" + } + ] + }, + "Wednesday": { + "E": [ + { + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "14:50 - 15:50" + } + ], + "D": [ + { + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "14:50 - 15:50" + } + ] + }, + "Thursday": { + "D": [ + { + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "14:50 - 15:50" + } + ] + }, + "Friday": {}, + "Saturday": {} }, - "AP24": { - "Monday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "SCS-501", - "time_slot": "2:15 - 3:10", - "classroom_id": "R1" - }, - { - "course": "Week 2", - "section": "B", - "subject_id": "SCS-501", - "time_slot": "11:10 - 12:05", - "classroom_id": "R2" - } - ], - "Tuesday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "SCS-501", - "time_slot": "9:55 - 10:50", - "classroom_id": "R1" - }, - { - "course": "Week 2", - "section": "C", - "subject_id": "SCS-501", - "time_slot": "1:20 - 2:15", - "classroom_id": "R3" - } - ], - "Wednesday": [ - { - "course": "Week 2", - "section": "B", - "subject_id": "SCS-501", - "time_slot": "9:00 - 9:55", - "classroom_id": "R2" - } - ], - "Thursday": [], - "Friday": [], - "Saturday": [] + "SG19": { + "Monday": {}, + "Tuesday": {}, + "Wednesday": { + "E": [ + { + "subject_id": "TCS-509", + "classroom_id": "R5", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "TCS-509", + "classroom_id": "R5", + "time_slot": "13:50 - 14:50" + } + ] + }, + "Thursday": { + "F": [ + { + "subject_id": "TCS-509", + "classroom_id": "R6", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "TCS-509", + "classroom_id": "R6", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Friday": {}, + "Saturday": {} }, - "VD25": { - "Monday": [], - "Tuesday": [], - "Wednesday": [], - "Thursday": [], - "Friday": [], - "Saturday": [] + "JM12": { + "Monday": { + "E": [ + { + "subject_id": "TMA-502", + "classroom_id": "R5", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Tuesday": { + "F": [ + { + "subject_id": "TMA-502", + "classroom_id": "R6", + "time_slot": "11:00 - 12:00" + } + ] + }, + "Wednesday": { + "F": [ + { + "subject_id": "TMA-502", + "classroom_id": "R6", + "time_slot": "12:00 - 13:00" + } + ], + "D": [ + { + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Thursday": { + "E": [ + { + "subject_id": "TMA-502", + "classroom_id": "R5", + "time_slot": "16:50 - 17:50" + } + ], + "C": [ + { + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Friday": { + "D": [ + { + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "11:00 - 12:00" + } + ], + "E": [ + { + "subject_id": "TMA-502", + "classroom_id": "R5", + "time_slot": "09:00 - 10:00" + } + ] + }, + "Saturday": {} + }, + "None": { + "Monday": {}, + "Tuesday": { + "D": [ + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Wednesday": { + "F": [ + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Thursday": { + "E": [ + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "16:50 - 17:50" + } + ], + "F": [ + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Friday": { + "D": [ + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "16:50 - 17:50" + } + ], + "E": [ + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "16:50 - 17:50" + } + ], + "F": [ + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Saturday": { + "A": [ + { + "subject_id": "Library", + "classroom_id": "R1", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "classroom_id": "R1", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "classroom_id": "R1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "classroom_id": "R1", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "classroom_id": "R1", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "classroom_id": "R1", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "classroom_id": "R1", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "classroom_id": "R1", + "time_slot": "12:00 - 13:00" + } + ], + "B": [ + { + "subject_id": "Library", + "classroom_id": "R2", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "classroom_id": "R2", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "classroom_id": "R2", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "classroom_id": "R2", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "classroom_id": "R2", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "classroom_id": "R2", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "classroom_id": "R2", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "classroom_id": "R2", + "time_slot": "12:00 - 13:00" + } + ], + "C": [ + { + "subject_id": "Library", + "classroom_id": "R3", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "classroom_id": "R3", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "classroom_id": "R3", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "classroom_id": "R3", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "classroom_id": "R3", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "classroom_id": "R3", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "classroom_id": "R3", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "classroom_id": "R3", + "time_slot": "12:00 - 13:00" + } + ], + "D": [ + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "16:50 - 17:50" + } + ], + "E": [ + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "16:50 - 17:50" + } + ], + "F": [ + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "16:50 - 17:50" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "08:00 - 09:00" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "09:00 - 10:00" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "11:00 - 12:00" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "12:00 - 13:00" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "14:50 - 15:50" + }, + { + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "16:50 - 17:50" + } + ] + } }, "AK26": { - "Monday": [], - "Tuesday": [ - { - "course": "Week 2", - "section": "A", - "subject_id": "Placement_Class", - "time_slot": "2:15 - 3:10", - "classroom_id": "R1" - } - ], - "Wednesday": [], - "Thursday": [], - "Friday": [], - "Saturday": [] + "Monday": { + "E": [ + { + "subject_id": "Placement_Class", + "classroom_id": "R5", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Placement_Class", + "classroom_id": "R5", + "time_slot": "14:50 - 15:50" + } + ] + }, + "Tuesday": {}, + "Wednesday": {}, + "Thursday": { + "D": [ + { + "subject_id": "Placement_Class", + "classroom_id": "R4", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Placement_Class", + "classroom_id": "R4", + "time_slot": "14:50 - 15:50" + } + ], + "F": [ + { + "subject_id": "Placement_Class", + "classroom_id": "R6", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Placement_Class", + "classroom_id": "R6", + "time_slot": "14:50 - 15:50" + } + ] + }, + "Friday": { + "D": [ + { + "subject_id": "Placement_Class", + "classroom_id": "R4", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Placement_Class", + "classroom_id": "R4", + "time_slot": "14:50 - 15:50" + } + ], + "E": [ + { + "subject_id": "Placement_Class", + "classroom_id": "R5", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Placement_Class", + "classroom_id": "R5", + "time_slot": "14:50 - 15:50" + } + ], + "F": [ + { + "subject_id": "Placement_Class", + "classroom_id": "R6", + "time_slot": "13:50 - 14:50" + }, + { + "subject_id": "Placement_Class", + "classroom_id": "R6", + "time_slot": "14:50 - 15:50" + } + ] + }, + "Saturday": {} + }, + "NJ13": { + "Monday": {}, + "Tuesday": {}, + "Wednesday": { + "F": [ + { + "subject_id": "TMA-502", + "classroom_id": "R6", + "time_slot": "16:50 - 17:50" + } + ] + }, + "Thursday": { + "E": [ + { + "subject_id": "TMA-502", + "classroom_id": "R5", + "time_slot": "12:00 - 13:00" + } + ] + }, + "Friday": { + "F": [ + { + "subject_id": "TMA-502", + "classroom_id": "R6", + "time_slot": "08:00 - 09:00" + } + ] + }, + "Saturday": {} } } \ No newline at end of file diff --git a/Constants/teacher_tt_csv.py b/Constants/teacher_tt_csv.py new file mode 100644 index 0000000..9f5418f --- /dev/null +++ b/Constants/teacher_tt_csv.py @@ -0,0 +1,81 @@ +import os +import json +import csv +from Samples.samples import (WorkingDays, SampleChromosome) +from Constants.teachers_tt import TeacherTimetable + +# Predefined time slots mapping +time_slots = { + 1: "08:00 - 09:00", + 2: "09:00 - 10:00", + 3: "11:00 - 12:00", + 4: "12:00 - 13:00", + 5: "16:50 - 17:50", +} + +# Reverse lookup to get slot number from time string +time_slot_order = {v: k for k, v in time_slots.items()} + +# List of days in the correct order +days_of_week_order = WorkingDays.days + +def extract_time_slots(timetable): + """Extract and sort time slots without inserting breaks or lunch.""" + unique_slots = set() + + # Collect all unique time slots from timetable + for day_data in timetable.values(): + for slots in day_data.values(): + for entry in slots: + unique_slots.add(entry["time_slot"]) + + # Sort slots based on predefined order + sorted_slots = sorted(unique_slots, key=lambda slot: time_slot_order.get(slot, float('inf'))) + + # Only include class slots + final_slots = [("CLASS", slot) for slot in sorted_slots] + + return final_slots + +def teacher_json_to_csv(teacher_timetable, output_folder): + """Convert teacher timetable dictionary to CSV.""" + os.makedirs(output_folder, exist_ok=True) + + for teacher_id, schedule in teacher_timetable.items(): + final_slots = extract_time_slots(schedule) + csv_file = os.path.join(output_folder, f"{teacher_id}.csv") + header = ["DAY"] + [slot[1] for slot in final_slots] + rows = [] + + # Sort days correctly + sorted_days = sorted(schedule.items(), key=lambda x: days_of_week_order.index(x[0])) + + for day, day_data in sorted_days: + row = [day] + ["" for _ in final_slots] # Empty slots initially + + for section, classes in day_data.items(): + for entry in classes: + for i, slot in enumerate(final_slots): + if entry["time_slot"] == slot[1]: + row[i + 1] = f"{entry['subject_id']} ({section}, {entry['classroom_id']})" + + rows.append(row) + + with open(csv_file, mode="w", newline="", encoding="utf-8") as file: + writer = csv.writer(file) + writer.writerow(header) + writer.writerows(rows) + +if __name__ == "__main__": + # Generate teacher-wise timetable + teacher_timetable = TeacherTimetable() + w = { + "Week 2": SampleChromosome.schedule2, + "Week 1": SampleChromosome.schedule1 + } + teacher_tt = teacher_timetable.generate_teacher_timetable(w) + + # Convert generated timetable to CSVs + output_folder = "tt_csvs" # Folder to save CSVs + teacher_json_to_csv(teacher_tt, output_folder) +w \ No newline at end of file diff --git a/Constants/Teachers_tt.py b/Constants/teachers_tt.py similarity index 55% rename from Constants/Teachers_tt.py rename to Constants/teachers_tt.py index a76498b..68b7377 100644 --- a/Constants/Teachers_tt.py +++ b/Constants/teachers_tt.py @@ -1,15 +1,21 @@ import json -from Samples.samples import (SubjectTeacherMap, WorkingDays, SampleChromosome) + +from Samples.samples import SampleChromosome, SubjectTeacherMap, WorkingDays + class TeacherTimetable: def __init__(self): # Create timetable for each teacher self.teacher_timetable = { - teacher: {day: [] for day in WorkingDays.days} - for teachers in SubjectTeacherMap.subject_teacher_map.values() + teacher: {day: [] for day in WorkingDays.days} + for teachers in SubjectTeacherMap.subject_teacher_map.values() + for teacher in teachers + } + self.teacher_assignments = { + teacher: {} + for teachers in SubjectTeacherMap.subject_teacher_map.values() for teacher in teachers } - self.teacher_assignments = {teacher: {} for teachers in SubjectTeacherMap.subject_teacher_map.values() for teacher in teachers} def generate_teacher_timetable(self, chromosome): """ @@ -26,18 +32,23 @@ def generate_teacher_timetable(self, chromosome): classroom_id = entry["classroom_id"] # Conflict check: Ensure the teacher is not already assigned at this time slot - if teacher_id in self.teacher_assignments and time_slot in self.teacher_assignments[teacher_id]: + if ( + teacher_id in self.teacher_assignments + and time_slot in self.teacher_assignments[teacher_id] + ): continue # Skip this assignment if there's a conflict # Add the class to the teacher's timetable - self.teacher_timetable[teacher_id][day].append({ - "course": week, # Include course information (Week 1, Week 2) - "section": section, - "subject_id": subject_id, - "time_slot": time_slot, - "classroom_id": classroom_id, - }) - + self.teacher_timetable[teacher_id][day].append( + { + "course": week, # Include course information (Week 1, Week 2) + "section": section, + "subject_id": subject_id, + "time_slot": time_slot, + "classroom_id": classroom_id, + } + ) + # Update the teacher's assignments to reflect the added class if teacher_id not in self.teacher_assignments: self.teacher_assignments[teacher_id] = {} @@ -46,34 +57,37 @@ def generate_teacher_timetable(self, chromosome): "section": section, "subject_id": subject_id, "classroom_id": classroom_id, - "day": day + "day": day, } - # Debug: Ensure each teacher is assigned a class - if teacher_id not in self.teacher_timetable: - print(f"Teacher {teacher_id} has no classes assigned.") + return self.teacher_timetable def save_timetable_to_json(self, file_path="Constants/teacher_timetable.json"): """ - Save the teacher's timetable to a JSON file. + Create a dictionary and save the teacher's timetable to a JSON file. """ try: + # Create a dictionary explicitly from the teacher timetable + timetable_dict = { + teacher: {day: classes for day, classes in days.items()} + for teacher, days in self.teacher_timetable.items() + } + + # Save the dictionary to a JSON file with open(file_path, "w") as json_file: - json.dump(self.teacher_timetable, json_file, indent=4) + json.dump(timetable_dict, json_file, indent=4) + + print(f"Timetable successfully saved to '{file_path}'.") except Exception as e: print(f"Error saving timetable to '{file_path}': {e}") if __name__ == "__main__": teacher_timetable = TeacherTimetable() - # Generate timetable from the sample chromosome (Week 1 and Week 2) - w = { - "Week 2": SampleChromosome.schedule2, - "Week 1": SampleChromosome.schedule1 - } - - teacher_timetable.generate_teacher_timetable(w) - + w = {"Week 2": SampleChromosome.schedule2, "Week 1": SampleChromosome.schedule1} + + teacher_tt = teacher_timetable.generate_teacher_timetable(w) + print(teacher_tt) # Save timetable to a JSON file - teacher_timetable.save_timetable_to_json() + teacher_timetable.save_timetable_to_json() diff --git a/Constants/time_intervals.py b/Constants/time_intervals.py index 1a6de92..39444a3 100644 --- a/Constants/time_intervals.py +++ b/Constants/time_intervals.py @@ -1,26 +1,44 @@ -from datetime import datetime +from datetime import datetime, timedelta class TimeIntervalConstant: - time_slots = { - 1: "9:00 - 9:55", - 2: "9:55 - 10:50", - 3: "11:10 - 12:05", - 4: "12:05 - 1:00", - 5: "1:20 - 2:15", - 6: "2:15 - 3:10", - 7: "3:30 - 4:25", - } - - time_mapping = { - "9:00 - 9:55": 1, - "9:55 - 10:50": 2, - "11:10 - 12:05": 3, - "12:05 - 1:00": 4, - "1:20 - 2:15": 5, - "2:15 - 3:10": 6, - "3:30 - 4:25": 7, - } + time_slots = {} + time_mapping = {} + + @staticmethod + def generate_dynamic_schedule( + start_time, + period_duration=55, + total_periods=7, + break_after_periods=None, + lunch_after_period=4, + break_duration=10, + lunch_duration=30, + ): + if break_after_periods is None: + break_after_periods = set() + TimeIntervalConstant.time_slots.clear() + TimeIntervalConstant.time_mapping.clear() + + start_time = datetime.strptime(start_time, "%H:%M") + + current_time = start_time + period_counter = 1 + + while period_counter <= total_periods: + end_time = current_time + timedelta(minutes=period_duration) + time_slot = ( + f"{current_time.strftime('%H:%M')} - {end_time.strftime('%H:%M')}" + ) + TimeIntervalConstant.time_slots[period_counter] = time_slot + TimeIntervalConstant.time_mapping[time_slot] = period_counter + current_time = end_time + if period_counter in break_after_periods: + current_time += timedelta(minutes=break_duration) + if period_counter == lunch_after_period: + current_time += timedelta(minutes=lunch_duration) + + period_counter += 1 @staticmethod def get_slot(slot_number: int) -> str: @@ -41,8 +59,9 @@ def get_slot(slot_number: int) -> str: else: max_slot = len(TimeIntervalConstant.time_slots) - raise ValueError(f"Invalid slot number. Please provide a slot number between 1 and {max_slot + 1}.") - + raise ValueError( + f"Invalid slot number. Please provide a slot number between 1 and {max_slot + 1}." + ) @classmethod def get_slot_number(cls, start_time: str, end_time: str): @@ -63,8 +82,12 @@ def get_slot_number(cls, start_time: str, end_time: str): for slot, interval in cls.time_slots.items(): # Making string in this format: "3:30 - 4:25". - slot_start_time = datetime.strptime(interval.split(" - ")[0], "%H:%M").time() - slot_end_time = datetime.strptime(interval.split(" - ")[1], "%H:%M").time() + slot_start_time = datetime.strptime( + interval.split(" - ")[0], "%H:%M" + ).time() + slot_end_time = datetime.strptime( + interval.split(" - ")[1], "%H:%M" + ).time() if start_time == slot_start_time and end_time == slot_end_time: return slot @@ -82,3 +105,23 @@ def get_all_slot_numbers(): def get_all_time_slots(): """Retrieve all time intervals.""" return list(TimeIntervalConstant.time_slots.values()) + + +TimeIntervalConstant.generate_dynamic_schedule( + "8:00", # Start time (positional argument) + 60, # Period duration (positional argument) + 7, # Total periods (positional argument) + {2, 6}, # Break after periods (positional argument) + 4, # Lunch after period (positional argument) + 60, # Break duration (positional argument) + 50, # Lunch duration (positional argument) +) + +slot_numbers = TimeIntervalConstant.get_all_slot_numbers() + + +time_slots = TimeIntervalConstant.get_all_time_slots() + + +# print("Slot Numbers:", slot_numbers) +# print("Time Slots:", time_slots) diff --git a/Constants/timetable_csv.py b/Constants/timetable_csv.py new file mode 100644 index 0000000..1b59b94 --- /dev/null +++ b/Constants/timetable_csv.py @@ -0,0 +1,124 @@ +import os +import json +import csv +from datetime import datetime + +# Predefined time slots mapping +time_slots = { + 1: "8:00 - 9:00", + 2: "9:00 - 10:00", + 3: "11:10 - 12:05", + 4: "12:05 - 1:00", + 5: "1:20 - 2:15", + 6: "2:15 - 3:10", + 7: "3:30 - 4:25", +} + +# Reverse lookup to get slot number from time string +time_slot_order = {v: k for k, v in time_slots.items()} + +# List of days in the order we want them to appear +days_of_week_order = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] + +def parse_time(time_slot): + """Extract start and end times from a time slot string.""" + start_time_str, end_time_str = time_slot.split(" - ") + start_time = datetime.strptime(start_time_str.strip(), "%I:%M").time() + end_time = datetime.strptime(end_time_str.strip(), "%I:%M").time() + return start_time, end_time + +def extract_time_slots(timetable): + """Extract, sort time slots, and insert BREAK/LUNCH where needed.""" + unique_slots = set() + + # Collect all unique time slots from timetable + for day_data in timetable.values(): + for slots in day_data.values(): + for entry in slots: + unique_slots.add(entry["time_slot"]) + + # Sort slots based on predefined order + sorted_slots = sorted(unique_slots, key=lambda slot: time_slot_order[slot]) + + # Insert BREAK and LUNCH dynamically based on gaps + final_slots = [] + last_end_time = None + break_lunch_counter = 0 + + for slot in sorted_slots: + start_time, end_time = parse_time(slot) + + if last_end_time: + gap = (datetime.combine(datetime.today(), start_time) - datetime.combine(datetime.today(), last_end_time)).seconds / 60 + + if gap > 5: # If there's a gap greater than 5 minutes + break_time = f"{last_end_time.strftime('%I:%M')} - {start_time.strftime('%I:%M')}" + if break_lunch_counter == 0: + final_slots.append(("BREAK", break_time)) + elif break_lunch_counter == 1: + final_slots.append(("LUNCH", break_time)) + else: + final_slots.append(("BREAK", break_time)) + + break_lunch_counter += 1 # Increment counter + + final_slots.append(("CLASS", slot)) + last_end_time = end_time # Update last_end_time + + return final_slots + +def json_to_csv(input_folder, json_filename, output_folder): + """Convert JSON timetable to CSV with breaks and lunches inserted.""" + json_file = os.path.join(input_folder, json_filename) + + with open(json_file, "r", encoding="utf-8") as file: + timetable = json.load(file) + + sections = set() + final_slots = extract_time_slots(timetable) + + os.makedirs(output_folder, exist_ok=True) + + # Identify all sections + for day_data in timetable.values(): + for section in day_data.keys(): + sections.add(section) + + # Sort days in the correct order + sorted_days = sorted(timetable.items(), key=lambda x: days_of_week_order.index(x[0])) + + for section in sections: + csv_file = os.path.join(output_folder, f"{section}.csv") + header = ["DAY"] + [slot[1] for slot in final_slots] # Extract only time for header + rows = [] + + # Iterate over sorted days in the correct order + for day, day_data in sorted_days: # Sorted days based on days_of_week_order + # Initialize row with blank spaces + row = [day] + [""] * len(final_slots) # Initially empty for each time slot + + if section in day_data: + for entry in day_data[section]: + for i, slot in enumerate(final_slots): + # Compare and add the class schedule, BREAK, or LUNCH + if entry["time_slot"] == slot[1] and slot[0] == "CLASS": + row[i + 1] = f"{entry['subject_id']} ({entry['teacher_id']}, {entry['classroom_id']})" + elif slot[0] == "BREAK" or slot[0] == "LUNCH": + # Add BREAK and LUNCH to appropriate slots + row[i + 1] = slot[0] + + rows.append(row) + + # Write the row into CSV + with open(csv_file, mode="w", newline="", encoding="utf-8") as file: + writer = csv.writer(file) + writer.writerow(header) + writer.writerows(rows) + + print(f"CSV file '{csv_file}' has been created successfully!") + +if __name__ == "_main_": + input_folder = "." # Your input folder where the JSON file is located + json_filename = "new.json" # Replace with your actual JSON filename + output_folder = "output_csvs" # Output folder where CSV files will be saved + json_to_csv(input_folder, json_filename, output_folder) \ No newline at end of file diff --git a/GA/__init__.py b/GA/__init__.py index 8b13789..d4633bd 100644 --- a/GA/__init__.py +++ b/GA/__init__.py @@ -1 +1,260 @@ +import copy +from dataclasses import dataclass +from Constants.helper_routines import ( + initialize_teacher_availability, + update_matrix_for_best, + update_teacher_availability_matrix, +) +from GA.chromosome import TimeTableGeneration +from GA.fitness import TimetableFitnessEvaluator +from GA.mutation import TimeTableCrossOver, TimeTableMutation +from GA.selection import TimeTableSelection + + +@dataclass +class TimetableConfig: + teacher_subject_mapping: dict + total_sections: dict + total_classrooms: dict + total_labs: dict + teacher_preferences: dict + teacher_weekly_workload: dict + special_subjects: dict + labs: dict + subject_quota_limits: dict + teacher_duty_days: dict + teacher_availability_matrix: dict + lab_availability_matrix: dict + total_generations: int + time_slots: dict + day_map: dict + time_slot_map: dict + prev_selected: dict = None + prev_mutated: list = None + fixed_teacher_assignment: dict = None + + +class TimetableEngine: + def __init__(self, config: TimetableConfig): + self.config = config + self.teacher_availability = copy.deepcopy(config.teacher_availability_matrix) + self.lab_availability = copy.deepcopy(config.lab_availability_matrix) + + def _update_lab_availability(self, best_timetable): + updated_lab = copy.deepcopy(self.config.lab_availability_matrix) + for weekday, daily_schedule in best_timetable.items(): + day_index = self.config.day_map.get(weekday) + if day_index is None: + continue + for allocations in daily_schedule.values(): + for alloc in allocations: + lab = alloc.get("classroom_id") + ts_index = self.config.time_slot_map.get(alloc.get("time_slot")) + if lab in updated_lab and ts_index is not None: + updated_lab[lab][day_index][ts_index - 1] = False + return updated_lab + + def _generate_timetable(self, teacher_matrix): + tg = TimeTableGeneration( + teacher_subject_mapping=self.config.teacher_subject_mapping, + total_sections=self.config.total_sections, + total_classrooms=self.config.total_classrooms, + total_labs=self.config.total_labs, + teacher_preferences=self.config.teacher_preferences, + teacher_weekly_workload=self.config.teacher_weekly_workload, + special_subjects=self.config.special_subjects, + labs=self.config.labs, + subject_quota_limits=self.config.subject_quota_limits, + teacher_duty_days=self.config.teacher_duty_days, + teacher_availability_matrix=teacher_matrix, + lab_availability_matrix=self.lab_availability, + time_slots=self.config.time_slots, + fixed_teacher_assignment=self.config.fixed_teacher_assignment or {} + ) + + timetable, updated_teacher = tg.create_timetable(self.config.total_generations)[:2] + + fitness = TimetableFitnessEvaluator( + timetable=timetable, + all_sections=list(tg.sections_manager.keys()), + subject_teacher_mapping=tg.subject_teacher_mapping, + available_classrooms=list(tg.classrooms_manager.keys()), + available_labs=list(tg.lab_capacity_manager.keys()), + classroom_capacity=tg.classrooms_manager, + section_student_strength=tg.sections_manager, + subject_quota_data=tg.subject_quota_limits, + teacher_time_preferences=tg.teacher_availability_preferences, + teacher_daily_workload=tg.weekly_workload, + time_slots=self.config.time_slots, + ).evaluate_timetable_fitness() + + selected = TimeTableSelection().select_chromosomes(fitness[1]) + if self.config.prev_selected: + selected.update(self.config.prev_selected) + + crossover = TimeTableCrossOver() + crossover_chromosomes = [] + selected_keys = list(selected.keys()) + for i in range(0, len(selected_keys), 2): + if i + 1 < len(selected_keys): + c1, c2 = crossover.perform_crossover( + timetable[selected_keys[i]], timetable[selected_keys[i + 1]] + ) + crossover_chromosomes.extend([c1, c2]) + + mutated = [TimeTableMutation().mutate_schedule_for_week(ch) for ch in crossover_chromosomes] + if self.config.prev_mutated: + mutated.extend(self.config.prev_mutated) + + best_chromosome, best_score = None, -1 + for key, score in selected.items(): + score = int(score) + if score > best_score and key in timetable: + best_score = score + best_chromosome = timetable[key] + + if best_chromosome: + updated_teacher = update_teacher_availability_matrix(teacher_matrix, best_chromosome) + return best_chromosome, updated_teacher, selected, mutated + + def run(self): + initial_teacher = copy.deepcopy(self.teacher_availability) + updated_teacher = copy.deepcopy(initial_teacher) + best_chromosome = None + + self.config.prev_selected = None + self.config.prev_mutated = None + + for _ in range(self.config.total_generations): + teacher_copy = copy.deepcopy(initial_teacher) + best, updated_teacher, selected, mutated = self._generate_timetable(teacher_copy) + best_chromosome = best + + self.config.prev_selected = selected + self.config.prev_mutated = mutated + + updated_teacher = update_matrix_for_best( + best_chromosome, + updated_teacher, + self.config.day_map, + self.config.time_slot_map, + ) + + updated_lab = self._update_lab_availability(best_chromosome) + return best_chromosome, updated_teacher, updated_lab + + +def run_timetable_generation( + teacher_subject_mapping: dict, + total_sections: dict, + total_classrooms: dict, + total_labs: dict, + teacher_preferences: dict, + teacher_weekly_workload: dict, + special_subjects: dict, + labs: dict, + subject_quota_limits: dict, + teacher_duty_days: dict, + teacher_availability_matrix: dict, + lab_availability_matrix: dict, + total_generations: int, + time_slots: dict, + day_map: dict, + time_slot_map: dict, + fixed_teacher_assignment: dict = None +): + config = TimetableConfig( + teacher_subject_mapping=teacher_subject_mapping, + total_sections=total_sections, + total_classrooms=total_classrooms, + total_labs=total_labs, + teacher_preferences=teacher_preferences, + teacher_weekly_workload=teacher_weekly_workload, + special_subjects=special_subjects, + labs=labs, + subject_quota_limits=subject_quota_limits, + teacher_duty_days=teacher_duty_days, + teacher_availability_matrix=teacher_availability_matrix, + lab_availability_matrix=lab_availability_matrix, + total_generations=total_generations, + time_slots=time_slots, + day_map=day_map, + time_slot_map=time_slot_map, + fixed_teacher_assignment=fixed_teacher_assignment or {} + ) + engine = TimetableEngine(config) + return engine.run() + + +if __name__ == "__main__": + from Constants.constant import Defaults + from Samples.samples import ( + SpecialSubjects, + SubjectTeacherMap, + SubjectWeeklyQuota, + TeacherWorkload, + ) + + lab_matrix = { + "L1": [[True] * 7 for _ in range(5)], + "L2": [[True] * 7 for _ in range(5)], + "L3": [[True] * 7 for _ in range(5)], + "L4": [[True] * 7 for _ in range(5)], + "L5": [[True] * 7 for _ in range(5)], + "L6": [[True] * 7 for _ in range(5)], + } + + teacher_availability = initialize_teacher_availability( + TeacherWorkload.Weekly_workLoad.keys(), 5, 7 + ) + + best_tt, final_teacher, final_lab = run_timetable_generation( + teacher_subject_mapping=SubjectTeacherMap.subject_teacher_map, + total_sections={"A": 70, "B": 100, "C": 75, "D": 100}, + total_classrooms={"R1": 200, "R2": 230, "R3": 240, "R4": 250, "R5": 250}, + total_labs={"L1": 70, "L2": 50, "L3": 70, "L4": 50, "L5": 70, "L6": 50}, + teacher_preferences=TeacherWorkload.teacher_preferences, + teacher_weekly_workload=TeacherWorkload.Weekly_workLoad, + special_subjects=SpecialSubjects.special_subjects, + labs=SpecialSubjects.Labs, + subject_quota_limits=SubjectWeeklyQuota.subject_quota, + teacher_duty_days=TeacherWorkload.teacher_duty_days, + teacher_availability_matrix=teacher_availability, + lab_availability_matrix=lab_matrix, + total_generations=Defaults.total_no_of_generations, + time_slots={ + 1: "9:00 - 9:55", + 2: "9:55 - 10:50", + 3: "11:10 - 12:05", + 4: "12:05 - 1:00", + 5: "1:20 - 2:15", + 6: "2:15 - 3:10", + 7: "3:30 - 4:25", + }, + day_map={ + "Monday": 0, + "Tuesday": 1, + "Wednesday": 2, + "Thursday": 3, + "Friday": 4, + "Saturday": 5, + "Sunday": 6, + }, + time_slot_map={ + "9:00 - 9:55": 1, + "9:55 - 10:50": 2, + "11:10 - 12:05": 3, + "12:05 - 1:00": 4, + "1:20 - 2:15": 5, + "2:15 - 3:10": 6, + "3:30 - 4:25": 7, + }, + fixed_teacher_assignment={ + "A": {"TCS-531": "AB01", "TMA-502": "HP18"}, + "B": {"TCS-503": "BJ10"} + } + ) + + from icecream import ic + ic(best_tt, final_teacher, final_lab) diff --git a/GA/chromosome.json b/GA/chromosome.json index 650e577..36a69ef 100644 --- a/GA/chromosome.json +++ b/GA/chromosome.json @@ -3,20 +3,20 @@ "Monday": { "A": [ { - "teacher_id": "SJ16", - "subject_id": "TCS-509", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R1", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R1", "time_slot": "11:10 - 12:05" }, @@ -29,90 +29,66 @@ ], "B": [ { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R2", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", - "classroom_id": "R2", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", - "classroom_id": "R2", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R2", - "time_slot": "2:15 - 3:10" + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "PK02", + "subject_id": "TCS-531", "classroom_id": "R2", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L1", + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "BJ10", - "subject_id": "TMA-502", + "teacher_id": "SJ16", + "subject_id": "TCS-509", "classroom_id": "R3", - "time_slot": "11:10 - 12:05" + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", + "teacher_id": "AA04", + "subject_id": "TCS-502", "classroom_id": "R3", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "1:20 - 2:15" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "RS11", + "subject_id": "TMA-502", "classroom_id": "R3", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "D": [ @@ -129,84 +105,126 @@ "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AC05", - "subject_id": "TCS-503", + "teacher_id": "JM12", + "subject_id": "TMA-502", "classroom_id": "R4", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AA04", + "teacher_id": "AC05", "subject_id": "TCS-502", "classroom_id": "R4", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AA15", + "subject_id": "PMA-502", + "classroom_id": "L3", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AA15", + "subject_id": "PMA-502", + "classroom_id": "L3", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "AC05", - "subject_id": "TCS-502", + "teacher_id": "PA21", + "subject_id": "XCS-501", "classroom_id": "R5", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "AK23", + "subject_id": "CSP-501", "classroom_id": "R5", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R5", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "NJ13", + "subject_id": "TMA-502", "classroom_id": "R5", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R5", + "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "NB22", - "subject_id": "XCS-501", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "NB22", + "subject_id": "XCS-501", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "PK02", + "subject_id": "TCS-531", "classroom_id": "R6", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "DP07", + "subject_id": "TCS-503", "classroom_id": "R6", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "AK23", + "subject_id": "CSP-501", "classroom_id": "R6", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", + "teacher_id": "AA04", + "subject_id": "TCS-502", "classroom_id": "R6", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "SG19", + "subject_id": "TCS-509", "classroom_id": "R6", "time_slot": "3:30 - 4:25" } @@ -215,432 +233,474 @@ "Tuesday": { "A": [ { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R1", - "time_slot": "11:10 - 12:05" + "time_slot": "9:00 - 9:55" }, { "teacher_id": "SS03", "subject_id": "TCS-502", "classroom_id": "R1", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R1", - "time_slot": "1:20 - 2:15" + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SJ16", - "subject_id": "TCS-509", + "teacher_id": "AK23", + "subject_id": "CSP-501", "classroom_id": "R1", - "time_slot": "2:15 - 3:10" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "DT20", + "subject_id": "XCS-501", "classroom_id": "R1", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "BJ10", - "subject_id": "TMA-502", + "teacher_id": "AP24", + "subject_id": "SCS-501", "classroom_id": "R2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "AK23", + "subject_id": "CSP-501", "classroom_id": "R2", "time_slot": "9:55 - 10:50" }, { "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L1", + "subject_id": "TMA-502", + "classroom_id": "R2", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "AA04", + "subject_id": "TCS-502", "classroom_id": "R2", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L2", + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-502", + "classroom_id": "R3", "time_slot": "9:55 - 10:50" }, { "teacher_id": "DP07", "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "9:00 - 9:55" + "classroom_id": "L1", + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L2", + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L1", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "PA21", - "subject_id": "XCS-501", - "classroom_id": "R4", + "teacher_id": "SP06", + "subject_id": "PCS-503", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "SP06", + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "SJ16", + "subject_id": "TCS-509", "classroom_id": "R4", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R4", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AA15", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "2:15 - 3:10" + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AA15", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "1:20 - 2:15" + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "NJ13", + "subject_id": "TMA-502", "classroom_id": "R4", "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "NB22", - "subject_id": "XCS-501", + "teacher_id": "AB17", + "subject_id": "TCS-509", "classroom_id": "R5", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "DP07", + "subject_id": "TCS-503", "classroom_id": "R5", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L2", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R5", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AK26", + "subject_id": "Placement_Class", + "classroom_id": "R5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R5", + "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "DP07", - "subject_id": "TCS-503", + "teacher_id": "RS11", + "subject_id": "TMA-502", "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "HP18", + "subject_id": "TCS-509", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AB17", - "subject_id": "TCS-509", + "teacher_id": "AP24", + "subject_id": "SCS-501", "classroom_id": "R6", "time_slot": "11:10 - 12:05" }, + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R6", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "NB22", + "subject_id": "XCS-501", + "classroom_id": "R6", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R6", + "time_slot": "2:15 - 3:10" + }, { "teacher_id": "AB01", "subject_id": "TCS-531", "classroom_id": "R6", - "time_slot": "12:05 - 1:00" + "time_slot": "3:30 - 4:25" } ] }, "Wednesday": { "A": [ { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "SJ16", + "subject_id": "TCS-509", "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R1", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L2", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R1", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R1", - "time_slot": "2:15 - 3:10" + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" }, { "teacher_id": "SS03", "subject_id": "TCS-502", "classroom_id": "R1", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "AB17", + "subject_id": "TCS-509", "classroom_id": "R2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R2", "time_slot": "9:55 - 10:50" }, - { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" - } - ], - "C": [ { "teacher_id": "RS11", "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" + "classroom_id": "L1", + "time_slot": "11:10 - 12:05" }, { "teacher_id": "RS11", "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R3", - "time_slot": "11:10 - 12:05" + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", - "classroom_id": "R3", + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R2", "time_slot": "12:05 - 1:00" - }, + } + ], + "C": [ { - "teacher_id": "BJ10", + "teacher_id": "NJ13", "subject_id": "TMA-502", "classroom_id": "R3", - "time_slot": "1:20 - 2:15" + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "DT20", + "subject_id": "XCS-501", "classroom_id": "R3", - "time_slot": "2:15 - 3:10" + "time_slot": "9:55 - 10:50" }, { "teacher_id": "AB01", "subject_id": "TCS-531", "classroom_id": "R3", - "time_slot": "3:30 - 4:25" - } + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" + } ], "D": [ { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "HP18", + "subject_id": "TCS-509", "classroom_id": "R4", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AC05", - "subject_id": "TCS-502", + "teacher_id": "PA21", + "subject_id": "XCS-501", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "DP07", + "subject_id": "TCS-503", "classroom_id": "R4", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "NB22", - "subject_id": "XCS-501", + "teacher_id": "AA04", + "subject_id": "TCS-502", "classroom_id": "R4", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "AC05", + "subject_id": "TCS-503", "classroom_id": "R5", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R5", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AB17", - "subject_id": "TCS-509", - "classroom_id": "R5", + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "NB22", + "subject_id": "XCS-501", "classroom_id": "R5", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R5", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", + "teacher_id": "RS11", + "subject_id": "TMA-502", "classroom_id": "R5", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "JM12", - "subject_id": "TMA-502", + "teacher_id": "SG19", + "subject_id": "TCS-509", "classroom_id": "R5", "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "SJ16", + "subject_id": "TCS-509", "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "NJ13", - "subject_id": "TMA-502", + "teacher_id": "PK02", + "subject_id": "TCS-531", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DP07", + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R6", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R6", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "VD25", "subject_id": "PCS-503", "classroom_id": "L3", - "time_slot": "12:05 - 1:00" + "time_slot": "1:20 - 2:15" }, { - "teacher_id": "DP07", + "teacher_id": "VD25", "subject_id": "PCS-503", "classroom_id": "L3", - "time_slot": "11:10 - 12:05" + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R6", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "3:30 - 4:25" } ] }, "Thursday": { "A": [ { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "DT20", + "subject_id": "XCS-501", "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, @@ -650,253 +710,271 @@ "classroom_id": "R1", "time_slot": "9:55 - 10:50" }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R1", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R1", - "time_slot": "12:05 - 1:00" - }, { "teacher_id": "AB01", "subject_id": "TCS-531", "classroom_id": "R1", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "SS03", - "subject_id": "TCS-502", - "classroom_id": "R1", - "time_slot": "2:15 - 3:10" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R1", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "DP07", + "subject_id": "TCS-503", "classroom_id": "R2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AA04", + "teacher_id": "SS03", "subject_id": "TCS-502", "classroom_id": "R2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AK26", - "subject_id": "Placement_Class", - "classroom_id": "R2", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "BJ10", - "subject_id": "TMA-502", + "teacher_id": "AB17", + "subject_id": "TCS-509", "classroom_id": "R2", - "time_slot": "2:15 - 3:10" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", + "teacher_id": "PA21", + "subject_id": "XCS-501", "classroom_id": "R2", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "PA21", - "subject_id": "XCS-501", - "classroom_id": "R3", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AB17", - "subject_id": "TCS-509", - "classroom_id": "R3", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", + "teacher_id": "NB22", + "subject_id": "XCS-501", + "classroom_id": "R3", "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "AC05", + "subject_id": "TCS-503", "classroom_id": "R4", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RD09", + "teacher_id": "AD08", "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "12:05 - 1:00" + "classroom_id": "L1", + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "RD09", + "teacher_id": "AD08", "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" - } - ], - "E": [ + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" + }, { - "teacher_id": "AC05", - "subject_id": "TCS-502", - "classroom_id": "R5", - "time_slot": "9:00 - 9:55" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "HP18", - "subject_id": "TCS-509", - "classroom_id": "R5", - "time_slot": "9:55 - 10:50" + "teacher_id": "AK26", + "subject_id": "Placement_Class", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" }, { - "teacher_id": "RS11", - "subject_id": "TMA-502", - "classroom_id": "R5", - "time_slot": "11:10 - 12:05" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AC05", - "subject_id": "TCS-503", - "classroom_id": "R5", - "time_slot": "12:05 - 1:00" - }, + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" + } + ], + "E": [ { - "teacher_id": "PM14", - "subject_id": "PMA-502", + "teacher_id": "RS11", + "subject_id": "PCS-503", "classroom_id": "L2", - "time_slot": "2:15 - 3:10" + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "PM14", - "subject_id": "PMA-502", + "teacher_id": "RS11", + "subject_id": "PCS-503", "classroom_id": "L2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R5", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "NB22", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "SG19", - "subject_id": "TCS-509", - "classroom_id": "R6", + "teacher_id": "AA15", + "subject_id": "PMA-502", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", - "classroom_id": "R6", + "teacher_id": "AA15", + "subject_id": "PMA-502", + "classroom_id": "L2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "JM12", - "subject_id": "TMA-502", + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AK26", + "subject_id": "Placement_Class", + "classroom_id": "R6", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "3:30 - 4:25" } ] }, "Friday": { "A": [ { - "teacher_id": "SJ16", - "subject_id": "TCS-509", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "SJ16", + "subject_id": "TCS-509", "classroom_id": "R1", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", + "teacher_id": "RS11", + "subject_id": "PCS-503", "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", - "classroom_id": "R1", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R1", - "time_slot": "2:15 - 3:10" + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" }, { "teacher_id": "SP06", "subject_id": "TCS-503", "classroom_id": "R1", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "AB17", + "subject_id": "TCS-509", "classroom_id": "R2", "time_slot": "9:00 - 9:55" }, @@ -907,154 +985,178 @@ "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "12:05 - 1:00" + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "11:10 - 12:05" + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "AC05", + "subject_id": "TCS-503", "classroom_id": "R3", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "HP18", + "subject_id": "TCS-509", "classroom_id": "R3", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AA15", - "subject_id": "PMA-502", + "teacher_id": "AD08", + "subject_id": "PCS-506", "classroom_id": "L1", - "time_slot": "12:05 - 1:00" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AA15", - "subject_id": "PMA-502", + "teacher_id": "AD08", + "subject_id": "PCS-506", "classroom_id": "L1", - "time_slot": "11:10 - 12:05" + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "NB22", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AC05", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "BJ10", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AB17", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "AC05", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "JM12", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "3:30 - 4:25" } @@ -1062,211 +1164,217 @@ }, "Saturday": { "A": [ - { - "teacher_id": "BJ10", - "subject_id": "TMA-502", - "classroom_id": "R1", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R1", - "time_slot": "9:55 - 10:50" - }, { "teacher_id": "PM14", "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" + "classroom_id": "L2", + "time_slot": "9:00 - 9:55" }, { "teacher_id": "PM14", "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "11:10 - 12:05" - } - ], - "B": [ - { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L3", + "classroom_id": "L2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:00 - 9:55" + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R2", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AB17", - "subject_id": "TCS-509", - "classroom_id": "R2", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R1", "time_slot": "12:05 - 1:00" } ], - "C": [ + "B": [ { "teacher_id": "AB01", "subject_id": "TCS-531", - "classroom_id": "R3", + "classroom_id": "R2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R3", + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L1", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R2", "time_slot": "12:05 - 1:00" + } + ], + "C": [ + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L1", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R3", "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L1", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "2:15 - 3:10" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L1", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AA15", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "12:05 - 1:00" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AA15", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "11:10 - 12:05" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "NB22", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "JM12", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "SG19", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "3:30 - 4:25" } @@ -1277,157 +1385,139 @@ "Monday": { "A": [ { - "teacher_id": "SJ16", - "subject_id": "TCS-509", + "teacher_id": "AK23", + "subject_id": "CSP-501", "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R1", "time_slot": "9:55 - 10:50" }, - { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R1", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R1", - "time_slot": "12:05 - 1:00" - }, { "teacher_id": "PM14", "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "2:15 - 3:10" + "classroom_id": "L1", + "time_slot": "11:10 - 12:05" }, { "teacher_id": "PM14", "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "1:20 - 2:15" + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "DT20", + "subject_id": "XCS-501", "classroom_id": "R1", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "AB17", - "subject_id": "TCS-509", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "RS11", + "subject_id": "TMA-502", "classroom_id": "R2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "12:05 - 1:00" + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "11:10 - 12:05" + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "AA04", + "subject_id": "TCS-502", "classroom_id": "R3", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AC05", - "subject_id": "TCS-502", + "teacher_id": "PA21", + "subject_id": "XCS-501", "classroom_id": "R3", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R3", + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" + }, { "teacher_id": "AK23", "subject_id": "CSP-501", "classroom_id": "R3", "time_slot": "12:05 - 1:00" - }, + } + ], + "D": [ { "teacher_id": "AD08", - "subject_id": "PCS-506", + "subject_id": "PMA-502", "classroom_id": "L2", - "time_slot": "2:15 - 3:10" + "time_slot": "9:00 - 9:55" }, { "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "BJ10", - "subject_id": "TMA-502", - "classroom_id": "R3", - "time_slot": "3:30 - 4:25" - } - ], - "D": [ - { - "teacher_id": "DP07", - "subject_id": "PCS-503", + "subject_id": "PMA-502", "classroom_id": "L2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "9:00 - 9:55" + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "AK23", + "subject_id": "CSP-501", "classroom_id": "R4", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "SG19", - "subject_id": "TCS-509", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R4", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "AP24", + "subject_id": "SCS-501", "classroom_id": "R4", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "JM12", + "subject_id": "TMA-502", "classroom_id": "R4", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "PA21", + "teacher_id": "NB22", "subject_id": "XCS-501", "classroom_id": "R4", "time_slot": "3:30 - 4:25" @@ -1435,8 +1525,8 @@ ], "E": [ { - "teacher_id": "NB22", - "subject_id": "XCS-501", + "teacher_id": "AC05", + "subject_id": "TCS-502", "classroom_id": "R5", "time_slot": "9:00 - 9:55" }, @@ -1447,228 +1537,282 @@ "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SP06", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "SP06", - "subject_id": "PCS-503", - "classroom_id": "L2", + "teacher_id": "NJ13", + "subject_id": "TMA-502", + "classroom_id": "R5", "time_slot": "11:10 - 12:05" - } - ], - "F": [ - { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R6", - "time_slot": "9:00 - 9:55" }, { "teacher_id": "DT20", "subject_id": "XCS-501", - "classroom_id": "R6", - "time_slot": "9:55 - 10:50" + "classroom_id": "R5", + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R5", + "time_slot": "3:30 - 4:25" + } + ], + "F": [ + { + "teacher_id": "VD25", + "subject_id": "PCS-503", "classroom_id": "L2", - "time_slot": "12:05 - 1:00" + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", + "teacher_id": "VD25", + "subject_id": "PCS-503", "classroom_id": "L2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R6", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R6", "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R6", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R6", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R6", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R6", + "time_slot": "3:30 - 4:25" } ] }, "Tuesday": { "A": [ { - "teacher_id": "SJ16", - "subject_id": "TCS-509", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "BJ10", - "subject_id": "TMA-502", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R1", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "12:05 - 1:00" + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "AB17", - "subject_id": "TCS-509", + "teacher_id": "DP07", + "subject_id": "TCS-503", "classroom_id": "R2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "AK23", + "subject_id": "CSP-501", "classroom_id": "R2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L3", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "SS03", - "subject_id": "TCS-502", - "classroom_id": "R2", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "RS11", + "subject_id": "TMA-502", "classroom_id": "R2", - "time_slot": "2:15 - 3:10" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "AP24", + "subject_id": "SCS-501", "classroom_id": "R2", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "JM12", + "subject_id": "TMA-502", "classroom_id": "R3", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R3", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "11:10 - 12:05" + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "RS11", - "subject_id": "TMA-502", - "classroom_id": "R4", + "teacher_id": "VD25", + "subject_id": "PCS-503", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SG19", - "subject_id": "TCS-509", - "classroom_id": "R4", + "teacher_id": "VD25", + "subject_id": "PCS-503", + "classroom_id": "L2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "12:05 - 1:00" + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L2", + "teacher_id": "NJ13", + "subject_id": "TMA-502", + "classroom_id": "R4", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AK26", + "subject_id": "Placement_Class", "classroom_id": "R4", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AC05", - "subject_id": "TCS-502", + "teacher_id": "DT20", + "subject_id": "XCS-501", "classroom_id": "R4", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "AC05", + "subject_id": "TCS-503", "classroom_id": "R4", "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L2", + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R5", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R5", - "time_slot": "11:10 - 12:05" + "time_slot": "9:55 - 10:50" }, { "teacher_id": "PA21", "subject_id": "XCS-501", "classroom_id": "R5", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R5", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R5", + "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "SP06", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "9:55 - 10:50" + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R6", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SP06", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "9:00 - 9:55" + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R6", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AC05", - "subject_id": "TCS-503", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R6", "time_slot": "11:10 - 12:05" }, @@ -1685,14 +1829,14 @@ "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "SG19", + "subject_id": "TCS-509", "classroom_id": "R6", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "SJ16", - "subject_id": "TCS-509", + "teacher_id": "PK02", + "subject_id": "TCS-531", "classroom_id": "R6", "time_slot": "3:30 - 4:25" } @@ -1701,70 +1845,52 @@ "Wednesday": { "A": [ { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "DT20", + "subject_id": "XCS-501", "classroom_id": "R1", "time_slot": "9:55 - 10:50" }, - { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R1", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R1", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R1", - "time_slot": "1:20 - 2:15" - }, { "teacher_id": "SS03", "subject_id": "TCS-502", "classroom_id": "R1", - "time_slot": "2:15 - 3:10" + "time_slot": "11:10 - 12:05" }, { "teacher_id": "BJ10", "subject_id": "TMA-502", "classroom_id": "R1", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "AB17", - "subject_id": "TCS-509", + "teacher_id": "PA21", + "subject_id": "XCS-501", "classroom_id": "R2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "SJ16", + "subject_id": "TCS-509", "classroom_id": "R2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "RS11", + "subject_id": "TMA-502", "classroom_id": "R2", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "AA04", + "subject_id": "TCS-502", "classroom_id": "R2", "time_slot": "12:05 - 1:00" } @@ -1777,251 +1903,275 @@ "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "AC05", + "subject_id": "TCS-502", "classroom_id": "R3", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "PK02", + "subject_id": "TCS-531", "classroom_id": "R3", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "PA21", + "teacher_id": "NB22", "subject_id": "XCS-501", "classroom_id": "R3", "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "AC05", - "subject_id": "TCS-502", - "classroom_id": "R3", - "time_slot": "3:30 - 4:25" } ], "D": [ { - "teacher_id": "NJ13", - "subject_id": "TMA-502", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R4", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "NB22", - "subject_id": "XCS-501", + "teacher_id": "NJ13", + "subject_id": "TMA-502", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R4", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R4", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L1", + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R5", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "12:05 - 1:00" + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R5", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L1", + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R5", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R5", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "VD25", + "subject_id": "PCS-503", + "classroom_id": "L3", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "BJ10", - "subject_id": "TMA-502", + "teacher_id": "VD25", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", "classroom_id": "R5", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "SG19", - "subject_id": "TCS-509", - "classroom_id": "R6", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "PK02", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AB01", "subject_id": "TCS-531", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R6", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-502", + "classroom_id": "R6", "time_slot": "12:05 - 1:00" }, { "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "11:10 - 12:05" + "subject_id": "TMA-502", + "classroom_id": "R6", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "SG19", + "subject_id": "TCS-509", + "classroom_id": "R6", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "3:30 - 4:25" } ] }, "Thursday": { "A": [ { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R1", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R1", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R1", - "time_slot": "12:05 - 1:00" + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "SJ16", + "subject_id": "TCS-509", "classroom_id": "R1", - "time_slot": "1:20 - 2:15" + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R1", - "time_slot": "2:15 - 3:10" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R1", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L1", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", + "teacher_id": "PM14", + "subject_id": "PMA-502", "classroom_id": "L2", - "time_slot": "12:05 - 1:00" + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PM14", - "subject_id": "PMA-502", + "teacher_id": "RD09", + "subject_id": "PCS-506", "classroom_id": "L1", - "time_slot": "2:15 - 3:10" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "PM14", - "subject_id": "PMA-502", + "teacher_id": "RD09", + "subject_id": "PCS-506", "classroom_id": "L1", - "time_slot": "1:20 - 2:15" + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R2", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "RD09", + "teacher_id": "AD08", "subject_id": "PCS-506", "classroom_id": "L2", - "time_slot": "9:55 - 10:50" + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "RD09", + "teacher_id": "AD08", "subject_id": "PCS-506", "classroom_id": "L2", - "time_slot": "9:00 - 9:55" + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "PK02", + "subject_id": "TCS-531", "classroom_id": "R3", - "time_slot": "11:10 - 12:05" + "time_slot": "9:55 - 10:50" }, { "teacher_id": "AB17", "subject_id": "TCS-509", "classroom_id": "R3", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R3", "time_slot": "12:05 - 1:00" } ], @@ -2033,302 +2183,356 @@ "time_slot": "9:00 - 9:55" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "AA04", + "subject_id": "TCS-502", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "12:05 - 1:00" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "11:10 - 12:05" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "BJ10", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AC05", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "NB22", - "subject_id": "XCS-501", - "classroom_id": "R5", + "teacher_id": "AA15", + "subject_id": "PMA-502", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "AA15", + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R5", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SG19", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AK26", + "subject_id": "Placement_Class", + "classroom_id": "R5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L3", + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "DP07", + "subject_id": "TCS-503", "classroom_id": "R6", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "3:30 - 4:25" } ] }, "Friday": { "A": [ { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "9:55 - 10:50" + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "9:00 - 9:55" + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "SJ16", + "subject_id": "TCS-509", "classroom_id": "R1", "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "BJ10", - "subject_id": "TMA-502", + "teacher_id": "DP07", + "subject_id": "TCS-503", "classroom_id": "R2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SJ16", - "subject_id": "TCS-509", + "teacher_id": "DT20", + "subject_id": "XCS-501", "classroom_id": "R2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L3", + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "PK02", + "subject_id": "TCS-531", "classroom_id": "R2", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L1", + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R3", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AD08", + "teacher_id": "PM14", "subject_id": "PMA-502", "classroom_id": "L1", - "time_slot": "9:00 - 9:55" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R3", - "time_slot": "11:10 - 12:05" + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "HP18", + "subject_id": "TCS-509", "classroom_id": "R3", "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "AA04", + "subject_id": "TCS-502", "classroom_id": "R4", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AB17", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "AA15", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AA15", - "subject_id": "PMA-502", - "classroom_id": "L1", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L3", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "JM12", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SG19", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AK26", - "subject_id": "Placement_Class", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "3:30 - 4:25" } @@ -2337,210 +2541,222 @@ "Saturday": { "A": [ { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SJ16", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R1", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R1", + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R1", + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L1", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", - "classroom_id": "R1", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "BJ10", - "subject_id": "TMA-502", - "classroom_id": "R1", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R1", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", - "classroom_id": "R2", + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AB17", - "subject_id": "TCS-509", + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R2", "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R3", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "SJ16", + "subject_id": "TCS-509", "classroom_id": "R3", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R3", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R3", "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "JM12", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AC05", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AK26", - "subject_id": "Placement_Class", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "NB22", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L3", - "time_slot": "9:00 - 9:55" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AC05", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "9:55 - 10:50" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "9:00 - 9:55" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "NJ13", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L3", - "time_slot": "2:15 - 3:10" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "1:20 - 2:15" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L3", - "time_slot": "1:20 - 2:15" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "3:30 - 4:25" } @@ -2551,210 +2767,216 @@ "Monday": { "A": [ { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "SJ16", + "subject_id": "TCS-509", "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "BJ10", - "subject_id": "TMA-502", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R1", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SJ16", - "subject_id": "TCS-509", + "teacher_id": "AK23", + "subject_id": "CSP-501", "classroom_id": "R1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "DT20", + "subject_id": "XCS-501", "classroom_id": "R1", "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L1", + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AB17", - "subject_id": "TCS-509", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R2", - "time_slot": "11:10 - 12:05" + "time_slot": "9:55 - 10:50" }, { "teacher_id": "AP24", "subject_id": "SCS-501", "classroom_id": "R2", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R2", "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "DP07", + "subject_id": "TCS-503", "classroom_id": "R3", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "RS11", + "subject_id": "TMA-502", "classroom_id": "R3", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L2", + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R3", "time_slot": "11:10 - 12:05" }, - { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "1:20 - 2:15" - }, { "teacher_id": "PK02", "subject_id": "TCS-531", "classroom_id": "R3", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "9:55 - 10:50" + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "9:00 - 9:55" + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "AP24", + "subject_id": "SCS-501", "classroom_id": "R4", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "AC05", + "subject_id": "TCS-503", "classroom_id": "R4", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "AA15", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R5", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AA15", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:00 - 9:55" + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R5", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "PK02", + "subject_id": "TCS-531", "classroom_id": "R5", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "SG19", - "subject_id": "TCS-509", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R5", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", + "teacher_id": "AK26", + "subject_id": "Placement_Class", "classroom_id": "R5", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "AK23", + "subject_id": "CSP-501", "classroom_id": "R5", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "NB22", - "subject_id": "XCS-501", + "teacher_id": "NJ13", + "subject_id": "TMA-502", "classroom_id": "R5", "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "AC05", - "subject_id": "TCS-503", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "AP24", + "subject_id": "SCS-501", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "12:05 - 1:00" + "teacher_id": "SG19", + "subject_id": "TCS-509", + "classroom_id": "R6", + "time_slot": "11:10 - 12:05" }, { "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" + "subject_id": "TCS-503", + "classroom_id": "R6", + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R6", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L3", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", "classroom_id": "R6", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "AK23", + "subject_id": "CSP-501", "classroom_id": "R6", "time_slot": "3:30 - 4:25" } @@ -2763,68 +2985,74 @@ "Tuesday": { "A": [ { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "DT20", + "subject_id": "XCS-501", "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "AP24", + "subject_id": "SCS-501", "classroom_id": "R1", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R1", "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L1", + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", + "teacher_id": "RS11", + "subject_id": "PCS-503", "classroom_id": "L1", - "time_slot": "9:00 - 9:55" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L2", + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L1", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "9:55 - 10:50" + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "9:00 - 9:55" + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" }, { "teacher_id": "AB01", @@ -2833,140 +3061,164 @@ "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "AB17", + "subject_id": "TCS-509", "classroom_id": "R3", "time_slot": "12:05 - 1:00" - }, + } + ], + "D": [ { - "teacher_id": "BJ10", - "subject_id": "TMA-502", - "classroom_id": "R3", - "time_slot": "1:20 - 2:15" + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R3", - "time_slot": "2:15 - 3:10" + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", - "classroom_id": "R3", - "time_slot": "3:30 - 4:25" - } - ], - "D": [ - { - "teacher_id": "AC05", - "subject_id": "TCS-503", + "teacher_id": "AK23", + "subject_id": "CSP-501", "classroom_id": "R4", - "time_slot": "9:00 - 9:55" + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "VD25", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "VD25", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "JM12", + "subject_id": "TMA-502", "classroom_id": "R4", - "time_slot": "9:55 - 10:50" + "time_slot": "12:05 - 1:00" }, { "teacher_id": "PK02", "subject_id": "TCS-531", "classroom_id": "R4", - "time_slot": "11:10 - 12:05" + "time_slot": "1:20 - 2:15" }, { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "AC05", + "subject_id": "TCS-503", "classroom_id": "R4", - "time_slot": "12:05 - 1:00" + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R5", + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R5", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AD08", + "teacher_id": "PM14", "subject_id": "PMA-502", "classroom_id": "L1", - "time_slot": "12:05 - 1:00" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AD08", + "teacher_id": "PM14", "subject_id": "PMA-502", "classroom_id": "L1", - "time_slot": "11:10 - 12:05" + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R5", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", "classroom_id": "R5", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "JM12", - "subject_id": "TMA-502", + "teacher_id": "HP18", + "subject_id": "TCS-509", "classroom_id": "R5", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R5", "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "SG19", + "subject_id": "TCS-509", "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "NJ13", - "subject_id": "TMA-502", + "teacher_id": "PK02", + "subject_id": "TCS-531", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "AC05", + "subject_id": "TCS-502", "classroom_id": "R6", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AC05", - "subject_id": "TCS-502", + "teacher_id": "NB22", + "subject_id": "XCS-501", "classroom_id": "R6", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AB17", - "subject_id": "TCS-509", + "teacher_id": "AK26", + "subject_id": "Placement_Class", "classroom_id": "R6", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "SP06", + "teacher_id": "DP07", "subject_id": "TCS-503", "classroom_id": "R6", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "NJ13", + "subject_id": "TMA-502", "classroom_id": "R6", "time_slot": "3:30 - 4:25" } @@ -2975,81 +3227,75 @@ "Wednesday": { "A": [ { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "SJ16", + "subject_id": "TCS-509", "classroom_id": "R1", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L3", + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R1", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "BJ10", - "subject_id": "TMA-502", - "classroom_id": "R1", - "time_slot": "2:15 - 3:10" + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R1", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "11:10 - 12:05" + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "AB17", - "subject_id": "TCS-509", - "classroom_id": "R3", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "9:55 - 10:50" + }, { "teacher_id": "PK02", "subject_id": "TCS-531", @@ -3057,190 +3303,214 @@ "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "AA04", + "subject_id": "TCS-502", "classroom_id": "R3", - "time_slot": "1:20 - 2:15" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "RS11", + "teacher_id": "JM12", "subject_id": "TMA-502", "classroom_id": "R3", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "SP06", - "subject_id": "TCS-503", - "classroom_id": "R3", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "DT20", + "subject_id": "XCS-501", "classroom_id": "R4", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "HP18", + "subject_id": "TCS-509", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L2", + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-502", + "classroom_id": "R4", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" + "teacher_id": "AK26", + "subject_id": "Placement_Class", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "NJ13", + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L3", + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R5", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R5", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", - "classroom_id": "R5", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L1", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AA15", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "2:15 - 3:10" + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R5", + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AA15", - "subject_id": "PMA-502", - "classroom_id": "L1", + "teacher_id": "SG19", + "subject_id": "TCS-509", + "classroom_id": "R5", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R5", "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "AC05", - "subject_id": "TCS-503", - "classroom_id": "R6", + "teacher_id": "AA15", + "subject_id": "PMA-502", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "AA15", + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "NB22", + "subject_id": "XCS-501", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "DP07", + "subject_id": "TCS-503", "classroom_id": "R6", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "JM12", - "subject_id": "TMA-502", + "teacher_id": "AA04", + "subject_id": "TCS-502", "classroom_id": "R6", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "VD25", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "VD25", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R6", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R6", + "time_slot": "3:30 - 4:25" } ] }, "Thursday": { "A": [ { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "SJ16", + "subject_id": "TCS-509", "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R1", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R1", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R1", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L1", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R1", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "BJ10", - "subject_id": "TMA-502", - "classroom_id": "R1", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R1", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L3", - "time_slot": "9:00 - 9:55" + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" }, { "teacher_id": "AA04", @@ -3249,8 +3519,8 @@ "time_slot": "11:10 - 12:05" }, { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "DT20", + "subject_id": "XCS-501", "classroom_id": "R2", "time_slot": "12:05 - 1:00" } @@ -3263,134 +3533,158 @@ "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AB17", + "teacher_id": "HP18", "subject_id": "TCS-509", "classroom_id": "R3", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "12:05 - 1:00" + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "SP06", - "subject_id": "TCS-503", - "classroom_id": "R4", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "HP18", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "SG19", "subject_id": "TCS-509", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "JM12", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "NJ13", - "subject_id": "TMA-502", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R5", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "RS11", + "subject_id": "TMA-502", "classroom_id": "R5", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L3", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "2:15 - 3:10" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L2", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "9:55 - 10:50" + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R6", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "9:00 - 9:55" + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R6", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SG19", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "NB22", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "3:30 - 4:25" } @@ -3399,28 +3693,28 @@ "Friday": { "A": [ { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L3", + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "12:05 - 1:00" + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L1", + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00" } ], "B": [ @@ -3431,31 +3725,37 @@ "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "RS11", + "subject_id": "TMA-502", "classroom_id": "R2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "BJ10", - "subject_id": "TMA-502", + "teacher_id": "PK02", + "subject_id": "TCS-531", "classroom_id": "R2", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "DP07", + "subject_id": "TCS-503", "classroom_id": "R2", "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "DP07", - "subject_id": "TCS-503", - "classroom_id": "R3", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "9:55 - 10:50" + }, { "teacher_id": "SJ16", "subject_id": "TCS-509", @@ -3463,8 +3763,8 @@ "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "AC05", + "subject_id": "TCS-503", "classroom_id": "R3", "time_slot": "11:10 - 12:05" }, @@ -3477,132 +3777,132 @@ ], "D": [ { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "2:15 - 3:10" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "1:20 - 2:15" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" }, { - "teacher_id": "NB22", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AC05", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "2:15 - 3:10" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "1:20 - 2:15" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AB17", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "12:05 - 1:00" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "11:10 - 12:05" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "3:30 - 4:25" } @@ -3611,210 +3911,234 @@ "Saturday": { "A": [ { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R1", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "BJ10", - "subject_id": "TMA-502", - "classroom_id": "R1", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R1", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", - "classroom_id": "R1", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R1", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R1", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "PK02", - "subject_id": "TCS-531", - "classroom_id": "R2", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "RS11", - "subject_id": "TMA-502", - "classroom_id": "R2", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AA15", + "subject_id": "PMA-502", + "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "AA15", + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R2", "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "AB17", - "subject_id": "TCS-509", - "classroom_id": "R3", + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "JM12", - "subject_id": "TMA-502", - "classroom_id": "R3", + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R3", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "DP07", - "subject_id": "TCS-503", - "classroom_id": "R3", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "AC05", - "subject_id": "TCS-502", + "teacher_id": "DT20", + "subject_id": "XCS-501", "classroom_id": "R3", - "time_slot": "1:20 - 2:15" + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R3", - "time_slot": "2:15 - 3:10" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R3", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "NJ13", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L2", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "9:55 - 10:50" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "9:00 - 9:55" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SG19", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AC05", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SJ16", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "NB22", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "3:30 - 4:25" } @@ -3825,37 +4149,43 @@ "Monday": { "A": [ { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "AP24", + "subject_id": "SCS-501", "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "DT20", + "subject_id": "XCS-501", "classroom_id": "R1", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R1", "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "PK02", - "subject_id": "TCS-531", - "classroom_id": "R2", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "9:55 - 10:50" + }, { "teacher_id": "AP24", "subject_id": "SCS-501", @@ -3863,43 +4193,37 @@ "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", - "classroom_id": "R2", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AA04", - "subject_id": "TCS-502", - "classroom_id": "R2", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AK26", - "subject_id": "Placement_Class", - "classroom_id": "R2", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R2", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "BJ10", - "subject_id": "TMA-502", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R2", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "AC05", - "subject_id": "TCS-502", - "classroom_id": "R3", + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "9:55 - 10:50" + }, { "teacher_id": "SJ16", "subject_id": "TCS-509", @@ -3907,22 +4231,22 @@ "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "AK23", + "subject_id": "CSP-501", "classroom_id": "R3", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R3", "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "JM12", - "subject_id": "TMA-502", + "teacher_id": "AB17", + "subject_id": "TCS-509", "classroom_id": "R4", "time_slot": "9:00 - 9:55" }, @@ -3933,102 +4257,126 @@ "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L2", + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R4", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R5", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "JM12", + "subject_id": "TMA-502", "classroom_id": "R5", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", + "teacher_id": "PA21", + "subject_id": "XCS-501", "classroom_id": "R5", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AB17", - "subject_id": "TCS-509", + "teacher_id": "AC05", + "subject_id": "TCS-503", "classroom_id": "R5", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AK26", - "subject_id": "Placement_Class", + "teacher_id": "AK23", + "subject_id": "CSP-501", "classroom_id": "R5", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "AP24", + "subject_id": "SCS-501", "classroom_id": "R5", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "HP18", + "subject_id": "TCS-509", "classroom_id": "R5", "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "NJ13", - "subject_id": "TMA-502", + "teacher_id": "PK02", + "subject_id": "TCS-531", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AD08", + "teacher_id": "AA15", "subject_id": "PMA-502", "classroom_id": "L1", - "time_slot": "12:05 - 1:00" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AD08", + "teacher_id": "AA15", "subject_id": "PMA-502", "classroom_id": "L1", - "time_slot": "11:10 - 12:05" + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "NJ13", + "subject_id": "TMA-502", + "classroom_id": "R6", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "SG19", + "subject_id": "TCS-509", "classroom_id": "R6", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AC05", - "subject_id": "TCS-503", + "teacher_id": "NB22", + "subject_id": "XCS-501", "classroom_id": "R6", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R6", "time_slot": "3:30 - 4:25" } @@ -4042,284 +4390,326 @@ "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, - { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "SP06", - "subject_id": "TCS-503", - "classroom_id": "R1", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "BJ10", - "subject_id": "TMA-502", - "classroom_id": "R1", - "time_slot": "12:05 - 1:00" - }, { "teacher_id": "AB01", "subject_id": "TCS-531", "classroom_id": "R1", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R1", - "time_slot": "2:15 - 3:10" + "time_slot": "9:55 - 10:50" }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R1", - "time_slot": "3:30 - 4:25" - } - ], - "B": [ { "teacher_id": "PM14", "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "9:55 - 10:50" + "classroom_id": "L1", + "time_slot": "11:10 - 12:05" }, { "teacher_id": "PM14", "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "9:00 - 9:55" + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L2", + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R1", "time_slot": "12:05 - 1:00" - }, + } + ], + "B": [ { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AK26", - "subject_id": "Placement_Class", + "teacher_id": "AK23", + "subject_id": "CSP-501", "classroom_id": "R2", - "time_slot": "1:20 - 2:15" + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AB17", - "subject_id": "TCS-509", + "teacher_id": "AA04", + "subject_id": "TCS-502", "classroom_id": "R2", - "time_slot": "2:15 - 3:10" + "time_slot": "11:10 - 12:05" }, { "teacher_id": "PK02", "subject_id": "TCS-531", "classroom_id": "R2", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L1", + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R3", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" + "teacher_id": "AC05", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L3", + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R3", "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "DP07", + "subject_id": "TCS-503", "classroom_id": "R4", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "RS11", + "subject_id": "TMA-502", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "SJ16", + "subject_id": "TCS-509", "classroom_id": "R4", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "NB22", + "subject_id": "XCS-501", "classroom_id": "R4", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R4", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", + "teacher_id": "PK02", + "subject_id": "TCS-531", "classroom_id": "R4", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "NB22", - "subject_id": "XCS-501", - "classroom_id": "R5", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "JM12", - "subject_id": "TMA-502", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", "classroom_id": "R5", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AD08", + "teacher_id": "AA15", "subject_id": "PMA-502", "classroom_id": "L1", - "time_slot": "12:05 - 1:00" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AD08", + "teacher_id": "AA15", "subject_id": "PMA-502", "classroom_id": "L1", - "time_slot": "11:10 - 12:05" + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R5", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R5", + "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L2", + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R6", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DP07", + "teacher_id": "VD25", "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "9:00 - 9:55" + "classroom_id": "L1", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "VD25", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R6", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R6", + "time_slot": "1:20 - 2:15" }, { "teacher_id": "AB01", "subject_id": "TCS-531", "classroom_id": "R6", - "time_slot": "11:10 - 12:05" + "time_slot": "2:15 - 3:10" }, { "teacher_id": "AC05", - "subject_id": "TCS-503", + "subject_id": "TCS-502", "classroom_id": "R6", - "time_slot": "12:05 - 1:00" + "time_slot": "3:30 - 4:25" } ] }, "Wednesday": { "A": [ { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R1", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R1", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "DT20", + "subject_id": "XCS-501", "classroom_id": "R2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SJ16", - "subject_id": "TCS-509", + "teacher_id": "DP07", + "subject_id": "TCS-503", "classroom_id": "R2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", - "classroom_id": "R2", + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "JM12", + "subject_id": "TMA-502", "classroom_id": "R2", "time_slot": "12:05 - 1:00" } ], "C": [ + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" + }, { "teacher_id": "PM14", "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" + "classroom_id": "L1", + "time_slot": "11:10 - 12:05" }, { "teacher_id": "PM14", "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R3", - "time_slot": "11:10 - 12:05" + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "NB22", + "teacher_id": "PA21", "subject_id": "XCS-501", "classroom_id": "R3", "time_slot": "12:05 - 1:00" @@ -4327,88 +4717,106 @@ ], "D": [ { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "NB22", + "subject_id": "XCS-501", "classroom_id": "R4", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "NJ13", + "subject_id": "TMA-502", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DP07", - "subject_id": "PCS-503", + "teacher_id": "RD09", + "subject_id": "PCS-506", "classroom_id": "L1", - "time_slot": "12:05 - 1:00" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "DP07", - "subject_id": "PCS-503", + "teacher_id": "RD09", + "subject_id": "PCS-506", "classroom_id": "L1", - "time_slot": "11:10 - 12:05" + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "SJ16", + "subject_id": "TCS-509", "classroom_id": "R4", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AA15", + "subject_id": "PMA-502", + "classroom_id": "L3", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "BJ10", - "subject_id": "TMA-502", - "classroom_id": "R4", + "teacher_id": "AA15", + "subject_id": "PMA-502", + "classroom_id": "L3", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "DP07", + "teacher_id": "AC05", "subject_id": "TCS-503", "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R4", "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "AC05", - "subject_id": "TCS-503", - "classroom_id": "R5", + "teacher_id": "VD25", + "subject_id": "PCS-503", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "VD25", + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", "classroom_id": "R5", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R5", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AB17", - "subject_id": "TCS-509", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R5", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "AK26", + "subject_id": "Placement_Class", "classroom_id": "R5", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R5", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "AB17", + "subject_id": "TCS-509", "classroom_id": "R5", "time_slot": "3:30 - 4:25" } @@ -4418,41 +4826,47 @@ "teacher_id": "AD08", "subject_id": "PCS-506", "classroom_id": "L2", - "time_slot": "9:55 - 10:50" + "time_slot": "9:00 - 9:55" }, { "teacher_id": "AD08", "subject_id": "PCS-506", "classroom_id": "L2", - "time_slot": "9:00 - 9:55" + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R6", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", "classroom_id": "R6", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "DP07", + "subject_id": "TCS-503", "classroom_id": "R6", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "JM12", - "subject_id": "TMA-502", + "teacher_id": "AA04", + "subject_id": "TCS-502", "classroom_id": "R6", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R6", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "3:30 - 4:25" } @@ -4461,634 +4875,652 @@ "Thursday": { "A": [ { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L3", + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "12:05 - 1:00" + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L2", + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", "time_slot": "11:10 - 12:05" }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00" + } + ], + "B": [ { "teacher_id": "SS03", "subject_id": "TCS-502", - "classroom_id": "R1", - "time_slot": "1:20 - 2:15" + "classroom_id": "R2", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SJ16", + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AB17", "subject_id": "TCS-509", - "classroom_id": "R1", - "time_slot": "2:15 - 3:10" + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" }, { "teacher_id": "BJ10", "subject_id": "TMA-502", - "classroom_id": "R1", - "time_slot": "3:30 - 4:25" + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" } ], - "B": [ - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "9:55 - 10:50" - }, + "C": [ { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L1", + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R3", "time_slot": "9:00 - 9:55" }, { "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L3", + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R3", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R2", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R2", - "time_slot": "2:15 - 3:10" - }, + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" + } + ], + "D": [ { "teacher_id": "AA04", "subject_id": "TCS-502", - "classroom_id": "R2", - "time_slot": "3:30 - 4:25" - } - ], - "C": [ + "classroom_id": "R4", + "time_slot": "9:00 - 9:55" + }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L3", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L3", - "time_slot": "9:00 - 9:55" + "teacher_id": "VD25", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "SP06", + "teacher_id": "VD25", "subject_id": "PCS-503", - "classroom_id": "L2", + "classroom_id": "L1", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "SP06", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" - } - ], - "D": [ - { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", - "time_slot": "9:00 - 9:55" + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "AK26", + "subject_id": "Placement_Class", "classroom_id": "R4", - "time_slot": "9:55 - 10:50" + "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "11:10 - 12:05" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "VD25", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "VD25", - "subject_id": "PCS-503", - "classroom_id": "L3", + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R5", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AA15", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" + "teacher_id": "AC05", + "subject_id": "TCS-502", + "classroom_id": "R5", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AA15", - "subject_id": "PMA-502", - "classroom_id": "L3", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AC05", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AB17", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "NB22", - "subject_id": "XCS-501", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "JM12", + "subject_id": "TMA-502", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AK26", + "subject_id": "Placement_Class", + "classroom_id": "R6", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "3:30 - 4:25" } ] }, "Friday": { "A": [ { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L3", + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { "teacher_id": "SJ16", "subject_id": "TCS-509", "classroom_id": "R1", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "BJ10", - "subject_id": "TMA-502", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R1", "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R2", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R2", - "time_slot": "12:05 - 1:00" + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "AA04", + "subject_id": "TCS-502", "classroom_id": "R2", - "time_slot": "1:20 - 2:15" + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "AB17", + "subject_id": "TCS-509", "classroom_id": "R2", - "time_slot": "2:15 - 3:10" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R2", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "C": [ - { - "teacher_id": "AA15", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AA15", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:00 - 9:55" - }, { "teacher_id": "PK02", "subject_id": "TCS-531", "classroom_id": "R3", - "time_slot": "11:10 - 12:05" + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AB17", - "subject_id": "TCS-509", + "teacher_id": "DP07", + "subject_id": "TCS-503", "classroom_id": "R3", - "time_slot": "12:05 - 1:00" + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", + "teacher_id": "RS11", + "subject_id": "PCS-503", "classroom_id": "L1", - "time_slot": "2:15 - 3:10" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", + "teacher_id": "RS11", + "subject_id": "PCS-503", "classroom_id": "L1", - "time_slot": "1:20 - 2:15" + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "JM12", + "subject_id": "TMA-502", "classroom_id": "R3", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "11:10 - 12:05" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "JM12", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "AC05", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SG19", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L2", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "3:30 - 4:25" } ] }, "Saturday": { "A": [ { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "SJ16", + "subject_id": "TCS-509", "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R1", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", - "classroom_id": "R1", + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R1", "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L1", + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", "time_slot": "9:00 - 9:55" }, { "teacher_id": "SP06", "subject_id": "TCS-503", "classroom_id": "R2", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "PK02", - "subject_id": "TCS-531", - "classroom_id": "R2", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "AA04", - "subject_id": "TCS-502", - "classroom_id": "R2", - "time_slot": "1:20 - 2:15" + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R2", - "time_slot": "2:15 - 3:10" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R2", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "NB22", - "subject_id": "XCS-501", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R3", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SJ16", + "teacher_id": "HP18", "subject_id": "TCS-509", "classroom_id": "R3", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "12:05 - 1:00" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "BJ10", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AC05", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AB17", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AC05", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "JM12", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "2:15 - 3:10" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "1:20 - 2:15" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "3:30 - 4:25" } @@ -5099,616 +5531,700 @@ "Monday": { "A": [ { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R1", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R1", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "BJ10", - "subject_id": "TMA-502", - "classroom_id": "R1", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R1", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R1", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R1", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "AK23", + "subject_id": "CSP-501", "classroom_id": "R2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "DP07", + "subject_id": "TCS-503", "classroom_id": "R2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", + "teacher_id": "RS11", + "subject_id": "PCS-503", "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R2", - "time_slot": "1:20 - 2:15" + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" }, { "teacher_id": "AA04", "subject_id": "TCS-502", "classroom_id": "R2", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "RS11", - "subject_id": "TMA-502", - "classroom_id": "R2", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "AC05", - "subject_id": "TCS-502", - "classroom_id": "R3", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R3", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "PK02", + "subject_id": "TCS-531", "classroom_id": "R3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AA15", + "subject_id": "PMA-502", + "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "NB22", - "subject_id": "XCS-501", + "teacher_id": "AA15", + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", "classroom_id": "R3", "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "DT20", + "subject_id": "XCS-501", "classroom_id": "R4", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", + "teacher_id": "AK23", + "subject_id": "CSP-501", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "JM12", - "subject_id": "TMA-502", + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AK26", + "subject_id": "Placement_Class", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", "classroom_id": "R4", - "time_slot": "11:10 - 12:05" + "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R4", - "time_slot": "12:05 - 1:00" + "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "AB17", + "subject_id": "TCS-509", "classroom_id": "R5", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "AC05", + "subject_id": "TCS-503", "classroom_id": "R5", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R5", + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L3", - "time_slot": "11:10 - 12:05" + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R5", + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "SJ16", - "subject_id": "TCS-509", + "teacher_id": "AK26", + "subject_id": "Placement_Class", "classroom_id": "R5", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AC05", - "subject_id": "TCS-503", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R5", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R5", "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "AA04", - "subject_id": "TCS-502", - "classroom_id": "R6", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L3", + "teacher_id": "VD25", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "VD25", + "subject_id": "PCS-503", + "classroom_id": "L1", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L3", - "time_slot": "11:10 - 12:05" - } - ] - }, - "Tuesday": { - "A": [ + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R6", + "time_slot": "12:05 - 1:00" + }, { "teacher_id": "AD08", "subject_id": "PCS-506", "classroom_id": "L3", - "time_slot": "9:55 - 10:50" + "time_slot": "1:20 - 2:15" }, { "teacher_id": "AD08", "subject_id": "PCS-506", "classroom_id": "L3", - "time_slot": "9:00 - 9:55" + "time_slot": "2:15 - 3:10" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", - "classroom_id": "R1", - "time_slot": "11:10 - 12:05" + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R6", + "time_slot": "2:15 - 3:10" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R1", - "time_slot": "12:05 - 1:00" - }, + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R6", + "time_slot": "3:30 - 4:25" + } + ] + }, + "Tuesday": { + "A": [ { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R1", - "time_slot": "1:20 - 2:15" + "time_slot": "9:00 - 9:55" }, { "teacher_id": "SJ16", "subject_id": "TCS-509", "classroom_id": "R1", - "time_slot": "2:15 - 3:10" + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", "classroom_id": "R1", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "AP24", + "subject_id": "SCS-501", "classroom_id": "R2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "BJ10", - "subject_id": "TMA-502", + "teacher_id": "DT20", + "subject_id": "XCS-501", "classroom_id": "R2", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "AB17", + "subject_id": "TCS-509", "classroom_id": "R2", "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "9:55 - 10:50" + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "9:00 - 9:55" + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "HP18", + "subject_id": "TCS-509", "classroom_id": "R3", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AA04", + "teacher_id": "SS03", "subject_id": "TCS-502", "classroom_id": "R3", "time_slot": "12:05 - 1:00" + } + ], + "D": [ + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", - "classroom_id": "R3", + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "VD25", + "subject_id": "PCS-503", + "classroom_id": "L3", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AB17", - "subject_id": "TCS-509", - "classroom_id": "R3", + "teacher_id": "VD25", + "subject_id": "PCS-503", + "classroom_id": "L3", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "RS11", - "subject_id": "TMA-502", - "classroom_id": "R3", + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "SG19", + "subject_id": "TCS-509", + "classroom_id": "R4", "time_slot": "3:30 - 4:25" } ], - "D": [ + "E": [ + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R5", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R5", + "time_slot": "9:55 - 10:50" + }, { "teacher_id": "RS11", "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" + "classroom_id": "L1", + "time_slot": "11:10 - 12:05" }, { "teacher_id": "RS11", "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "9:00 - 9:55" + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" }, { "teacher_id": "JM12", "subject_id": "TMA-502", - "classroom_id": "R4", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "NB22", - "subject_id": "XCS-501", - "classroom_id": "R4", + "classroom_id": "R5", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R4", + "teacher_id": "NB22", + "subject_id": "XCS-501", + "classroom_id": "R5", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", - "classroom_id": "R4", + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R5", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R4", + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R5", "time_slot": "3:30 - 4:25" } ], - "E": [ + "F": [ { - "teacher_id": "NJ13", - "subject_id": "TMA-502", - "classroom_id": "R5", + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AC05", - "subject_id": "TCS-502", - "classroom_id": "R5", + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L2", + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R6", "time_slot": "11:10 - 12:05" - } - ], - "F": [ + }, { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R6", + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:00 - 9:55" + "teacher_id": "NJ13", + "subject_id": "TMA-502", + "classroom_id": "R6", + "time_slot": "1:20 - 2:15" }, { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R6", - "time_slot": "11:10 - 12:05" + "time_slot": "2:15 - 3:10" }, { "teacher_id": "PK02", "subject_id": "TCS-531", "classroom_id": "R6", - "time_slot": "12:05 - 1:00" + "time_slot": "3:30 - 4:25" } ] }, "Wednesday": { "A": [ { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L2", + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "12:05 - 1:00" + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L2", + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R2", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R2", + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "12:05 - 1:00" + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L2", + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "AC05", + "subject_id": "TCS-502", "classroom_id": "R3", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SJ16", + "teacher_id": "HP18", "subject_id": "TCS-509", "classroom_id": "R3", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L3", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "SP06", - "subject_id": "TCS-503", - "classroom_id": "R3", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "PA21", + "subject_id": "XCS-501", "classroom_id": "R3", - "time_slot": "2:15 - 3:10" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "AP24", + "subject_id": "SCS-501", "classroom_id": "R3", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R4", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R4", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "SG19", + "subject_id": "TCS-509", "classroom_id": "R4", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", - "classroom_id": "R4", + "teacher_id": "AD08", + "subject_id": "PMA-502", + "classroom_id": "L3", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AB17", - "subject_id": "TCS-509", + "teacher_id": "AD08", + "subject_id": "PMA-502", + "classroom_id": "L3", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", "classroom_id": "R4", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "9:55 - 10:50" + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R5", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "9:00 - 9:55" + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R5", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AD08", + "teacher_id": "AA15", "subject_id": "PMA-502", "classroom_id": "L1", - "time_slot": "12:05 - 1:00" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AD08", + "teacher_id": "AA15", "subject_id": "PMA-502", "classroom_id": "L1", - "time_slot": "11:10 - 12:05" + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "NB22", + "subject_id": "XCS-501", + "classroom_id": "R5", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R5", + "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "DP07", + "subject_id": "TCS-503", "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "BJ10", - "subject_id": "TMA-502", + "teacher_id": "AC05", + "subject_id": "TCS-502", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "NB22", - "subject_id": "XCS-501", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R6", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "JM12", + "subject_id": "TMA-502", "classroom_id": "R6", "time_slot": "12:05 - 1:00" }, @@ -5719,14 +6235,14 @@ "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AC05", - "subject_id": "TCS-502", + "teacher_id": "AB17", + "subject_id": "TCS-509", "classroom_id": "R6", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "3:30 - 4:25" } @@ -5741,204 +6257,216 @@ "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R1", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R1", "time_slot": "12:05 - 1:00" - }, + } + ], + "B": [ { "teacher_id": "AD08", "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "2:15 - 3:10" + "classroom_id": "L2", + "time_slot": "9:00 - 9:55" }, { "teacher_id": "AD08", "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "SP06", - "subject_id": "TCS-503", - "classroom_id": "R1", - "time_slot": "3:30 - 4:25" - } - ], - "B": [ - { - "teacher_id": "RS11", - "subject_id": "PCS-503", "classroom_id": "L2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "9:00 - 9:55" + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "AA04", + "subject_id": "TCS-502", "classroom_id": "R2", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "RS11", + "subject_id": "TMA-502", "classroom_id": "R2", "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AB17", + "teacher_id": "HP18", "subject_id": "TCS-509", "classroom_id": "R3", - "time_slot": "11:10 - 12:05" + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "JM12", + "subject_id": "TMA-502", "classroom_id": "R3", - "time_slot": "12:05 - 1:00" + "time_slot": "9:55 - 10:50" }, { "teacher_id": "DP07", "subject_id": "TCS-503", "classroom_id": "R3", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R3", - "time_slot": "2:15 - 3:10" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "DT20", + "subject_id": "XCS-501", "classroom_id": "R3", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "NB22", - "subject_id": "XCS-501", + "teacher_id": "AC05", + "subject_id": "TCS-502", "classroom_id": "R4", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "SP06", + "subject_id": "TCS-503", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L2", + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "SP06", - "subject_id": "PCS-503", + "teacher_id": "RD09", + "subject_id": "PCS-506", "classroom_id": "L2", - "time_slot": "9:55 - 10:50" + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SP06", - "subject_id": "PCS-503", + "teacher_id": "RD09", + "subject_id": "PCS-506", "classroom_id": "L2", - "time_slot": "9:00 - 9:55" + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R5", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L2", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L2", + "teacher_id": "SG19", + "subject_id": "TCS-509", + "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "BJ10", + "teacher_id": "NJ13", "subject_id": "TMA-502", "classroom_id": "R6", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R6", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "2:15 - 3:10" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "1:20 - 2:15" }, { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "1:20 - 2:15" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "3:30 - 4:25" } @@ -5947,210 +6475,222 @@ "Friday": { "A": [ { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "DT20", + "subject_id": "XCS-501", "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R1", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", - "classroom_id": "R1", + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "SJ16", - "subject_id": "TCS-509", + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R1", "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R2", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R2", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "11:10 - 12:05" + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AB17", - "subject_id": "TCS-509", + "teacher_id": "JM12", + "subject_id": "TMA-502", "classroom_id": "R2", - "time_slot": "1:20 - 2:15" + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "PA21", + "subject_id": "XCS-501", "classroom_id": "R2", - "time_slot": "2:15 - 3:10" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "PK02", + "subject_id": "TCS-531", "classroom_id": "R2", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "C": [ { "teacher_id": "DP07", - "subject_id": "TCS-503", - "classroom_id": "R3", + "subject_id": "PCS-503", + "classroom_id": "L2", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R3", + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L2", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AC05", - "subject_id": "TCS-502", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R3", - "time_slot": "11:10 - 12:05" + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "BJ10", + "teacher_id": "NJ13", "subject_id": "TMA-502", "classroom_id": "R3", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "1:20 - 2:15" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "AC05", + "subject_id": "TCS-503", "classroom_id": "R3", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AC05", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L1", + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "11:10 - 12:05" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SG19", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "NB22", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AP24", - "subject_id": "SCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "JM12", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "SJ16", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "3:30 - 4:25" } @@ -6159,210 +6699,210 @@ "Saturday": { "A": [ { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "AB01", + "subject_id": "TCS-531", "classroom_id": "R1", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "BJ10", + "subject_id": "TMA-502", "classroom_id": "R1", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "SP06", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R1", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "SJ16", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R1", "time_slot": "12:05 - 1:00" } ], "B": [ { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R2", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AB17", - "subject_id": "TCS-509", + "teacher_id": "PK02", + "subject_id": "TCS-531", "classroom_id": "R2", - "time_slot": "12:05 - 1:00" + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AK26", - "subject_id": "Placement_Class", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R2", - "time_slot": "1:20 - 2:15" + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R2", - "time_slot": "2:15 - 3:10" + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "DP07", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R2", - "time_slot": "3:30 - 4:25" + "time_slot": "12:05 - 1:00" } ], "C": [ { - "teacher_id": "BJ10", + "teacher_id": "RS11", "subject_id": "TMA-502", "classroom_id": "R3", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "DT20", - "subject_id": "XCS-501", + "teacher_id": "SS03", + "subject_id": "TCS-502", "classroom_id": "R3", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "HP18", - "subject_id": "TCS-509", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R3", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R3", "time_slot": "12:05 - 1:00" } ], "D": [ { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "9:55 - 10:50" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "9:00 - 9:55" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AB01", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "AA04", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "2:15 - 3:10" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AD08", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "1:20 - 2:15" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R4", "time_slot": "3:30 - 4:25" } ], "E": [ { - "teacher_id": "AA15", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "9:00 - 9:55" }, { - "teacher_id": "AA15", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:00 - 9:55" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "9:55 - 10:50" }, { - "teacher_id": "AC05", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "11:10 - 12:05" }, { - "teacher_id": "PA21", - "subject_id": "XCS-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R5", "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R5", + "time_slot": "3:30 - 4:25" } ], "F": [ { - "teacher_id": "RS11", - "subject_id": "TMA-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "9:00 - 9:55" }, { - "teacher_id": "SS03", - "subject_id": "TCS-502", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "9:55 - 10:50" }, { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "12:05 - 1:00" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "11:10 - 12:05" }, { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" + "teacher_id": "None", + "subject_id": "Library", + "classroom_id": "R6", + "time_slot": "12:05 - 1:00" }, { - "teacher_id": "AC05", - "subject_id": "TCS-503", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "1:20 - 2:15" }, { - "teacher_id": "AK23", - "subject_id": "CSP-501", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "2:15 - 3:10" }, { - "teacher_id": "PK02", - "subject_id": "TCS-531", + "teacher_id": "None", + "subject_id": "Library", "classroom_id": "R6", "time_slot": "3:30 - 4:25" } diff --git a/GA/chromosome.py b/GA/chromosome.py index 74c86b4..69c7daf 100644 --- a/GA/chromosome.py +++ b/GA/chromosome.py @@ -1,223 +1,351 @@ +import copy import random -from Constants.time_intervals import TimeIntervalConstant -from Constants.constant import ( - Sections, - Classrooms, - RoomCapacity, - Defaults, -) +from math import ceil + +from Constants.constant import Defaults + class TimeTableGeneration: def __init__( self, teacher_subject_mapping: dict, - total_sections: int, - total_classrooms: int, - total_labs: int, + total_sections: dict, + total_classrooms: dict, + total_labs: dict, teacher_preferences: dict, teacher_weekly_workload: dict, special_subjects: dict, + labs: dict, subject_quota_limits: dict, - labs_list: list, teacher_duty_days: dict, + teacher_availability_matrix: dict, + lab_availability_matrix: dict, + time_slots: dict, + fixed_teacher_assignment: dict = None, ): - self.sections_manager = Sections(total_sections) - self.classrooms_manager = Classrooms(total_classrooms, total_labs) - self.room_capacity_manager = RoomCapacity( - self.classrooms_manager.classrooms, self.sections_manager.sections - ) + self.sections_manager = total_sections + self.classrooms_manager = total_classrooms + self.lab_capacity_manager = total_labs self.weekdays = Defaults.working_days self.subject_teacher_mapping = teacher_subject_mapping self.subject_quota_limits = subject_quota_limits - self.lab_subject_list = labs_list + self.lab_subject_list = labs self.special_subject_list = special_subjects self.teacher_availability_preferences = teacher_preferences - self.available_time_slots = TimeIntervalConstant.time_slots + self.available_time_slots = time_slots self.teacher_duty_days = teacher_duty_days self.weekly_workload = teacher_weekly_workload - - self.teacher_assignment_tracker = {teacher: 0 for teacher in teacher_weekly_workload.keys()} + self.teacher_availability_matrix = teacher_availability_matrix + self.initial_lab_availability_matrix = copy.deepcopy(lab_availability_matrix) + self.lab_availability_matrix = copy.deepcopy(self.initial_lab_availability_matrix) + self.fixed_teacher_assignment = fixed_teacher_assignment or {} self._map_sections_to_classrooms() - self._teacher_section() - def _map_sections_to_classrooms(self): - """Map sections to classrooms based on capacity.""" + def _map_sections_to_classrooms(self) -> dict: sorted_classrooms = sorted( - self.classrooms_manager.classrooms, - key=lambda c: self.room_capacity_manager.room_capacity[c], - reverse=True, + self.classrooms_manager.items(), + key = lambda item: item[1], + reverse=True ) sorted_sections = sorted( - self.sections_manager.sections, - key=lambda s: self.room_capacity_manager.section_strength[s], - reverse=True, + self.sections_manager.items(), + key = lambda item: item[1], + reverse = True ) + section_classroom_map = {} - self.section_to_classroom_map = {} - for section in sorted_sections: - for classroom in sorted_classrooms: - if self.room_capacity_manager.room_capacity[classroom] >= self.room_capacity_manager.section_strength[section]: - self.section_to_classroom_map[section] = classroom - sorted_classrooms.remove(classroom) + for section, student_count in sorted_sections: + for index, (classroom, capacity) in enumerate(sorted_classrooms): + if capacity >= student_count: + section_classroom_map[section] = classroom + sorted_classrooms.pop(index) break - else: - raise ValueError( - f"No classroom can accommodate section {section} with strength {self.room_capacity_manager.section_strength[section]}." - ) - - def _teacher_section(self): - """Map sections to teachers and subjects, ensuring balanced assignments.""" - self.teacher_section_map = { - section: {subject: None for subject in self.subject_teacher_mapping.keys()} - for section in self.sections_manager.sections - } - for section in self.sections_manager.sections: - for subject, teachers in self.subject_teacher_mapping.items(): - # Select teacher with the least workload - teachers_with_least_load = sorted( - teachers, key=lambda teacher: self.teacher_assignment_tracker[teacher] - ) - selected_teacher = teachers_with_least_load[0] - self.teacher_section_map[section][subject] = selected_teacher - self.teacher_assignment_tracker[selected_teacher] += 1 + self.section_to_classroom_map = section_classroom_map + return section_classroom_map - def _initialize_teacher_workload_tracker(self): - """Initialize a tracker for teacher workloads.""" - return {teacher: 0 for teacher in self.weekly_workload.keys()} - def _generate_section_schedule( - self, section, half_day_section_list, section_subject_usage_tracker, teacher_workload_tracker - ): - """Generate a schedule for an individual section.""" - section_schedule = [] - subjects_scheduled_today = set() - assigned_classroom = self.section_to_classroom_map[section] - total_slots_for_section = 4 if section in half_day_section_list else 7 + def _initialize_teacher_workload_tracker(self) -> dict: + return { + teacher: 0 + for teacher in self.weekly_workload + } - for slot_index, current_time_slot in enumerate(self.available_time_slots.values(), start=1): - if slot_index > total_slots_for_section: - break - assigned_teacher, selected_subject, assigned_room = self._assign_subject_and_teacher( - section, - slot_index, - subjects_scheduled_today, - assigned_classroom, - section_subject_usage_tracker, - teacher_workload_tracker, - ) - section_schedule.append( - { - "teacher_id": assigned_teacher, - "subject_id": selected_subject, - "classroom_id": assigned_room, - "time_slot": current_time_slot, - } - ) - if selected_subject != "Library": - section_subject_usage_tracker[section][selected_subject] += 1 + def _get_available_subjects(self, section: str, subject_usage_tracker: dict) -> list: + return [ + subject + for subject in self.subject_teacher_mapping + if subject_usage_tracker[section][subject] < self.subject_quota_limits.get(subject, 0) + ] - # Handle double-slot subjects - if selected_subject in self.lab_subject_list and slot_index + 1 <= total_slots_for_section: - next_time_slot = self.available_time_slots.get(slot_index + 1) - section_schedule.append( - { - "teacher_id": assigned_teacher, - "subject_id": selected_subject, - "classroom_id": assigned_room, - "time_slot": next_time_slot, - } - ) - section_subject_usage_tracker[section][selected_subject] += 1 - return section_schedule def _assign_subject_and_teacher( self, - section, - slot_index, - subjects_scheduled_today, - assigned_classroom, - section_subject_usage_tracker, - teacher_workload_tracker, - ): - """Assign a subject and teacher for a specific slot.""" - available_subjects = self._get_available_subjects(section, section_subject_usage_tracker) + section: str, + slot_index: int, + subjects_scheduled_today: set, + assigned_classroom: str, + subject_usage_tracker: dict, + teacher_workload_tracker: dict, + teacher_availability_matrix: dict, + day_index: int, + ) -> tuple: + available_subjects = self._get_available_subjects(section, subject_usage_tracker) random.shuffle(available_subjects) - assigned_teacher = None selected_subject = None - assigned_room = None + assigned_room = assigned_classroom for subject in available_subjects: - if subject in self.lab_subject_list and slot_index not in [1, 3, 5]: - continue - if subject in self.special_subject_list and slot_index not in [5]: + if (subject in self.lab_subject_list or subject == "Placement_Class") and slot_index not in [1, 3, 5]: continue - if subject not in subjects_scheduled_today: - teachers_for_subject = self.subject_teacher_mapping[subject] - teachers_with_least_load = sorted( - teachers_for_subject, key=lambda teacher: teacher_workload_tracker[teacher] - ) - assigned_teacher = teachers_with_least_load[0] - teacher_workload_tracker[assigned_teacher] += 1 - selected_subject = subject - subjects_scheduled_today.add(subject) - assigned_room = ( - self.classrooms_manager.labs[slot_index % len(self.classrooms_manager.labs)] - if subject in self.lab_subject_list - else assigned_classroom - ) + # First, check for a fixed teacher assignment for (section, subject) + fixed_teacher = self.fixed_teacher_assignment.get(section, {}).get(subject) + if fixed_teacher: + if ( + fixed_teacher in teacher_availability_matrix and + len(teacher_availability_matrix[fixed_teacher]) > day_index and + len(teacher_availability_matrix[fixed_teacher][day_index]) > (slot_index - 1) and + teacher_availability_matrix[fixed_teacher][day_index][slot_index - 1] + ): + assigned_teacher = fixed_teacher + teacher_workload_tracker[fixed_teacher] += 1 + selected_subject = subject + subjects_scheduled_today.add(subject) + break + + # Otherwise, assign normally from the available pool + teachers = self.subject_teacher_mapping[subject] + preferred_teachers = [ + t for t in teachers if self.teacher_availability_preferences.get(t, []) + ] + for teacher in sorted(preferred_teachers, key=lambda t: teacher_workload_tracker[t]): + if ( + teacher in teacher_availability_matrix and + len(teacher_availability_matrix[teacher]) > day_index and + len(teacher_availability_matrix[teacher][day_index]) > (slot_index - 1) and + teacher_availability_matrix[teacher][day_index][slot_index - 1] + ): + assigned_teacher = teacher + teacher_workload_tracker[teacher] += 1 + selected_subject = subject + subjects_scheduled_today.add(subject) + break + + if assigned_teacher: break if not assigned_teacher: selected_subject = "Library" assigned_teacher = "None" - assigned_room = assigned_classroom - return assigned_teacher, selected_subject, assigned_room - def _get_available_subjects(self, section, section_subject_usage_tracker): - """Get a list of subjects available for a specific section.""" - return [ - subject - for subject in self.subject_teacher_mapping.keys() - if section_subject_usage_tracker[section][subject] < self.subject_quota_limits.get(subject, 0) - ] - def generate_daily_schedule(self, half_day_section_list, section_subject_usage_tracker): - """Generate the daily schedule for all sections.""" - daily_schedule = {} - teacher_workload_tracker = self._initialize_teacher_workload_tracker() + def _allocate_lab( + self, + teacher: str, + subject: str, + day_index: int, + slot_index: int, + section_strength: int, + ) -> tuple: + group1_size = ceil(section_strength / 2) + group2_size = section_strength - group1_size + labs_list = list(self.lab_availability_matrix.keys()) + + for i in range(len(labs_list)): + lab1 = labs_list[i] + if ( + self.lab_availability_matrix[lab1][day_index][slot_index - 1] + and self.lab_availability_matrix[lab1][day_index][slot_index] + and self.lab_capacity_manager.get(lab1, 0) >= group1_size + ): + for j in range(i + 1, len(labs_list)): + lab2 = labs_list[j] + if ( + self.lab_availability_matrix[lab2][day_index][slot_index - 1] + and self.lab_availability_matrix[lab2][day_index][slot_index] + and self.lab_capacity_manager.get(lab2, 0) >= group2_size + ): + self.lab_availability_matrix[lab1][day_index][ + slot_index - 1 + ] = False + self.lab_availability_matrix[lab1][day_index][ + slot_index + ] = False + self.lab_availability_matrix[lab2][day_index][ + slot_index - 1 + ] = False + self.lab_availability_matrix[lab2][day_index][ + slot_index + ] = False + entries = [ + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": lab1, + "time_slot": self.available_time_slots[slot_index], + "group": 1, + }, + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": lab1, + "time_slot": self.available_time_slots[slot_index + 1], + "group": 1, + }, + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": lab2, + "time_slot": self.available_time_slots[slot_index], + "group": 2, + }, + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": lab2, + "time_slot": self.available_time_slots[slot_index + 1], + "group": 2, + }, + ] + return entries, slot_index + 2 + + merged_entry = { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": "merged_lab", + "time_slot": self.available_time_slots[slot_index], + "group": "merged", + "flagged": False, + } + return [merged_entry], slot_index + 1 + + + def _generate_section_schedule( + self, + section: str, + half_day_sections: list, + subject_usage_tracker: dict, + teacher_workload_tracker: dict, + teacher_availability_matrix: dict, + day_index: int, + section_strength: int, + ) -> tuple: + schedule = [] + subjects_scheduled = set() + assigned_classroom = self.section_to_classroom_map[section] + total_slots = 4 if section in half_day_sections else 7 + slot_index = 1 + + while slot_index <= total_slots: + teacher, subject, room = self._assign_subject_and_teacher( + section, + slot_index, + subjects_scheduled, + assigned_classroom, + subject_usage_tracker, + teacher_workload_tracker, + teacher_availability_matrix, + day_index, + ) + time_slot = self.available_time_slots[slot_index] + if subject in self.lab_subject_list or subject == "Placement_Class": + if slot_index <= total_slots - 1: + lab_entries, slot_index = self._allocate_lab( + teacher, subject, day_index, slot_index, section_strength + ) + schedule.extend(lab_entries) + subject_usage_tracker[section][subject] += len(lab_entries) + else: + schedule.append( + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": assigned_classroom, + "time_slot": time_slot, + "group": "fallback", + } + ) + subject_usage_tracker[section][subject] += 1 + slot_index += 1 + else: + schedule.append( + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": room, + "time_slot": time_slot, + "group": "all", + } + ) + if subject != "Library": + subject_usage_tracker[section][subject] += 1 + slot_index += 1 + + return schedule, teacher_availability_matrix - for section in self.sections_manager.sections: - section_schedule = self._generate_section_schedule( - section, half_day_section_list, section_subject_usage_tracker, teacher_workload_tracker + + def generate_daily_schedule( + self, + sections: list, + half_day_sections: list, + subject_usage_tracker: dict, + day_index: int, + ) -> tuple: + daily_schedule = {} + teacher_workload = self._initialize_teacher_workload_tracker() + for section in sections: + section_strength = self.sections_manager[section] + ( + schedule, + self.teacher_availability_matrix, + ) = self._generate_section_schedule( + section, + half_day_sections, + subject_usage_tracker, + teacher_workload, + self.teacher_availability_matrix, + day_index, + section_strength, ) - daily_schedule[section] = section_schedule + daily_schedule[section] = schedule + return daily_schedule, subject_usage_tracker, self.teacher_availability_matrix - return daily_schedule, section_subject_usage_tracker - def _generate_weekly_schedule(self): - """Generate a schedule for a single week.""" - week_schedule = {} - section_subject_usage_tracker = { + def _generate_weekly_schedule(self) -> tuple: + weekly_schedule = {} + subject_usage = { section: {subject: 0 for subject in self.subject_teacher_mapping.keys()} - for section in self.sections_manager.sections + for section in self.sections_manager.keys() } - for week_day in self.weekdays: - half_day_sections = self.sections_manager.sections[: len(self.sections_manager.sections) // 2] - daily_schedule, section_subject_usage_tracker = self.generate_daily_schedule( - half_day_sections, section_subject_usage_tracker + sections = list(self.sections_manager.keys()) + for day_index, weekday in enumerate(self.weekdays): + random.shuffle(sections) + half_day = sections[: len(sections) // 2] + ( + daily_sched, + subject_usage, + self.teacher_availability_matrix, + ) = self.generate_daily_schedule( + sections, half_day, subject_usage, day_index ) - week_schedule[week_day] = daily_schedule - return week_schedule, section_subject_usage_tracker + weekly_schedule[weekday] = daily_sched + return weekly_schedule, subject_usage, self.teacher_availability_matrix - def create_timetable(self, num_weeks): - """Create the timetable for the given number of weeks.""" + + def create_timetable(self, num_weeks: int) -> tuple: timetable = {} for week in range(1, num_weeks + 1): - week_schedule, section_subject_usage_tracker = self._generate_weekly_schedule() - timetable[f"Week {week}"] = week_schedule - return timetable + self.lab_availability_matrix = copy.deepcopy( + self.initial_lab_availability_matrix + ) + ( + weekly_schedule, + _, + self.teacher_availability_matrix, + ) = self._generate_weekly_schedule() + timetable[f"Week {week}"] = weekly_schedule + return timetable, self.teacher_availability_matrix, self.lab_availability_matrix diff --git a/GA/fitness.json b/GA/fitness.json index 9032c90..8f67637 100644 --- a/GA/fitness.json +++ b/GA/fitness.json @@ -5,51 +5,6 @@ "A": 995, "B": 960, "C": 1000, -<<<<<<< HEAD - "D": 960, - "E": 955, - "F": 900 - }, - "Tuesday": { - "A": 1000, - "B": 935, - "C": 930, - "D": 950, - "E": 910, - "F": 905 - }, - "Wednesday": { - "A": 915, - "B": 850, - "C": 895, - "D": 870, - "E": 780, - "F": 900 - }, - "Thursday": { - "A": 855, - "B": 850, - "C": 820, - "D": 785, - "E": 790, - "F": 850 - }, - "Friday": { - "A": 735, - "B": 770, - "C": 860, - "D": 785, - "E": 700, - "F": 750 - }, - "Saturday": { - "A": 570, - "B": 650, - "C": 590, - "D": 470, - "E": 400, - "F": 475 -======= "D": 920, "E": 995, "F": 920 @@ -93,59 +48,10 @@ "D": 325, "E": 325, "F": 325 ->>>>>>> 6a716a08517c777806fe28ece2e5c4f10034d9f9 } }, "Week 2": { "Monday": { -<<<<<<< HEAD - "A": 995, - "B": 1000, - "C": 960, - "D": 920, - "E": 1000, - "F": 970 - }, - "Tuesday": { - "A": 910, - "B": 935, - "C": 1000, - "D": 940, - "E": 970, - "F": 930 - }, - "Wednesday": { - "A": 900, - "B": 945, - "C": 880, - "D": 930, - "E": 805, - "F": 870 - }, - "Thursday": { - "A": 815, - "B": 790, - "C": 910, - "D": 760, - "E": 850, - "F": 790 - }, - "Friday": { - "A": 790, - "B": 840, - "C": 780, - "D": 755, - "E": 750, - "F": 750 - }, - "Saturday": { - "A": 445, - "B": 600, - "C": 600, - "D": 530, - "E": 650, - "F": 410 -======= "A": 850, "B": 850, "C": 850, @@ -192,59 +98,10 @@ "D": 215, "E": 215, "F": 215 ->>>>>>> 6a716a08517c777806fe28ece2e5c4f10034d9f9 } }, "Week 3": { "Monday": { -<<<<<<< HEAD - "A": 1000, - "B": 1000, - "C": 960, - "D": 1000, - "E": 960, - "F": 955 - }, - "Tuesday": { - "A": 970, - "B": 930, - "C": 985, - "D": 930, - "E": 930, - "F": 980 - }, - "Wednesday": { - "A": 905, - "B": 830, - "C": 890, - "D": 860, - "E": 865, - "F": 840 - }, - "Thursday": { - "A": 775, - "B": 840, - "C": 900, - "D": 870, - "E": 810, - "F": 830 - }, - "Friday": { - "A": 800, - "B": 810, - "C": 810, - "D": 760, - "E": 720, - "F": 750 - }, - "Saturday": { - "A": 460, - "B": 670, - "C": 520, - "D": 560, - "E": 640, - "F": 490 -======= "A": 740, "B": 735, "C": 700, @@ -291,19 +148,10 @@ "D": 205, "E": 205, "F": 205 ->>>>>>> 6a716a08517c777806fe28ece2e5c4f10034d9f9 } }, "Week 4": { "Monday": { -<<<<<<< HEAD - "A": 1000, - "B": 960, - "C": 960, - "D": 1000, - "E": 935, - "F": 890 -======= "A": 730, "B": 725, "C": 730, @@ -326,49 +174,8 @@ "D": 725, "E": 670, "F": 530 ->>>>>>> 6a716a08517c777806fe28ece2e5c4f10034d9f9 - }, - "Tuesday": { - "A": 935, - "B": 910, - "C": 1000, - "D": 960, - "E": 920, - "F": 975 - }, - "Wednesday": { - "A": 910, - "B": 880, - "C": 870, - "D": 880, - "E": 790, - "F": 925 }, "Thursday": { -<<<<<<< HEAD - "A": 780, - "B": 720, - "C": 860, - "D": 825, - "E": 920, - "F": 860 - }, - "Friday": { - "A": 830, - "B": 715, - "C": 830, - "D": 770, - "E": 755, - "F": 780 - }, - "Saturday": { - "A": 655, - "B": 510, - "C": 610, - "D": 590, - "E": 580, - "F": 405 -======= "A": 635, "B": 640, "C": 635, @@ -391,59 +198,10 @@ "D": 205, "E": 205, "F": 205 ->>>>>>> 6a716a08517c777806fe28ece2e5c4f10034d9f9 } }, "Week 5": { "Monday": { -<<<<<<< HEAD - "A": 995, - "B": 960, - "C": 960, - "D": 995, - "E": 890, - "F": 970 - }, - "Tuesday": { - "A": 930, - "B": 925, - "C": 930, - "D": 895, - "E": 930, - "F": 920 - }, - "Wednesday": { - "A": 960, - "B": 870, - "C": 885, - "D": 760, - "E": 850, - "F": 835 - }, - "Thursday": { - "A": 870, - "B": 810, - "C": 795, - "D": 820, - "E": 840, - "F": 745 - }, - "Friday": { - "A": 890, - "B": 790, - "C": 730, - "D": 810, - "E": 780, - "F": 795 - }, - "Saturday": { - "A": 610, - "B": 505, - "C": 630, - "D": 375, - "E": 650, - "F": 480 -======= "A": 725, "B": 730, "C": 690, @@ -490,23 +248,14 @@ "D": 205, "E": 205, "F": 205 ->>>>>>> 6a716a08517c777806fe28ece2e5c4f10034d9f9 } } }, "weekly_fitness_scores": { -<<<<<<< HEAD - "Week 1": 29315, - "Week 2": 29675, - "Week 3": 29805, - "Week 4": 29695, - "Week 5": 29385 -======= "Week 1": 26355, "Week 2": 20885, "Week 3": 19340, "Week 4": 19450, "Week 5": 19730 ->>>>>>> 6a716a08517c777806fe28ece2e5c4f10034d9f9 } } \ No newline at end of file diff --git a/GA/fitness.py b/GA/fitness.py index 7dcdb65..0153f8a 100644 --- a/GA/fitness.py +++ b/GA/fitness.py @@ -1,11 +1,13 @@ import json -from Constants.time_intervals import TimeIntervalConstant + +from Constants.constant import Defaults, PenaltyConstants from GA.chromosome import TimeTableGeneration -from Constants.constant import ( - PenaltyConstants, - Defaults, +from Samples.samples import ( + SpecialSubjects, + SubjectTeacherMap, + SubjectWeeklyQuota, + TeacherWorkload, ) -from Samples.samples import SubjectTeacherMap, SpecialSubjects, SubjectWeeklyQuota, Classrooms, TeacherWorkload class TimetableFitnessEvaluator: @@ -21,9 +23,13 @@ def __init__( subject_quota_data, teacher_time_preferences, teacher_daily_workload, + time_slots, + config=None, ): self.timetable = timetable - self.available_days = Defaults.working_days # Removed trailing comma + self.defaults = Defaults(config) + self.penalty_constants = PenaltyConstants(config) + self.available_days = self.defaults.working_days self.all_sections = all_sections self.subject_teacher_mapping = subject_teacher_mapping self.available_classrooms = available_classrooms @@ -33,11 +39,13 @@ def __init__( self.subject_quota_data = subject_quota_data self.teacher_time_preferences = teacher_time_preferences self.teacher_daily_workload = teacher_daily_workload + self.time_slots = time_slots def evaluate_timetable_fitness(self): daily_section_fitness_scores = {} weekly_fitness_scores = {} teacher_workload_tracking = {} + for week, week_schedule in self.timetable.items(): weekly_fitness = 0 daily_section_fitness_scores[week] = {} @@ -46,58 +54,82 @@ def evaluate_timetable_fitness(self): daily_section_fitness_scores[week][day] = {} day_fitness = 0 for section, section_schedule in day_schedule.items(): - section_fitness = Defaults.starting_section_fitness + section_fitness = self.defaults.starting_section_fitness classroom_time_slot_tracking = {} for schedule_item in section_schedule: assigned_teacher = schedule_item["teacher_id"] assigned_classroom = schedule_item["classroom_id"] - assigned_time_slot = TimeIntervalConstant.time_mapping.get( + assigned_time_slot = self.time_slots.get( schedule_item["time_slot"] ) section_strength = self.section_student_strength.get(section, 0) # Penalty 1: Double booking a teacher in the same time slot - if (assigned_teacher, assigned_time_slot) in teacher_time_slot_tracking.keys(): - section_fitness -= PenaltyConstants.PENALTY_TEACHER_DOUBLE_BOOKED + if ( + assigned_teacher, + assigned_time_slot, + ) in teacher_time_slot_tracking.keys(): + section_fitness -= ( + self.penalty_constants.PENALTY_TEACHER_DOUBLE_BOOKED + ) else: - teacher_time_slot_tracking[(assigned_teacher, assigned_time_slot)] = section + teacher_time_slot_tracking[ + (assigned_teacher, assigned_time_slot) + ] = section # Penalty 2: Double booking a classroom in the same time slot - if (assigned_classroom, assigned_time_slot) in classroom_time_slot_tracking: - section_fitness -= PenaltyConstants.PENALTY_CLASSROOM_DOUBLE_BOOKED + if ( + assigned_classroom, + assigned_time_slot, + ) in classroom_time_slot_tracking: + section_fitness -= ( + self.penalty_constants.PENALTY_CLASSROOM_DOUBLE_BOOKED + ) else: - classroom_time_slot_tracking[(assigned_classroom, assigned_time_slot)] = section + classroom_time_slot_tracking[ + (assigned_classroom, assigned_time_slot) + ] = section # Penalty 3: Over-capacity classrooms if section_strength > self.classroom_capacity.get( - assigned_classroom, Defaults.max_class_capacity + assigned_classroom, self.defaults.max_class_capacity ): - section_fitness -= PenaltyConstants.PENALTY_OVER_CAPACITY + section_fitness -= self.penalty_constants.PENALTY_OVER_CAPACITY # Penalty 4: Assigning teachers during unpreferred time slots preferred_time_slots = self.teacher_time_preferences.get( assigned_teacher, [] ) if assigned_time_slot not in preferred_time_slots: - section_fitness -= PenaltyConstants.PENALTY_UN_PREFERRED_SLOT + section_fitness -= ( + self.penalty_constants.PENALTY_UN_PREFERRED_SLOT + ) # Penalty 5: Scheduling teacher on a non-duty day assigned_day = day - if assigned_day not in TeacherWorkload.teacher_duty_days.get(assigned_teacher, []): - section_fitness -= PenaltyConstants.PENALTY_NON_DUTY_DAY + if assigned_day not in TeacherWorkload.teacher_duty_days.get( + assigned_teacher, [] + ): + section_fitness -= self.penalty_constants.PENALTY_NON_DUTY_DAY # Tracking teacher workload if assigned_teacher not in teacher_workload_tracking: teacher_workload_tracking[assigned_teacher] = [] - teacher_workload_tracking[assigned_teacher].append(assigned_time_slot) + teacher_workload_tracking[assigned_teacher].append( + assigned_time_slot + ) # Penalty 6: Exceeding teacher daily workload for teacher, times_assigned in teacher_workload_tracking.items(): if teacher is not None: - if len(times_assigned) > self.teacher_daily_workload.get(teacher, 0): - section_fitness -= PenaltyConstants.PENALTY_OVERLOAD_TEACHER + if len(times_assigned) > self.teacher_daily_workload.get( + teacher, 0 + ): + section_fitness -= ( + self.penalty_constants.PENALTY_OVERLOAD_TEACHER + ) daily_section_fitness_scores[week][day][section] = section_fitness day_fitness += section_fitness @@ -124,7 +156,6 @@ def evaluate_timetable_fitness(self): teacher_weekly_workload=TeacherWorkload.Weekly_workLoad, special_subjects=SpecialSubjects.special_subjects, subject_quota_limits=SubjectWeeklyQuota.subject_quota, - labs_list=SpecialSubjects.Labs, teacher_duty_days=TeacherWorkload.teacher_duty_days, ) generated_timetables = timetable_generator.create_timetable(5) @@ -143,7 +174,10 @@ def evaluate_timetable_fitness(self): timetable_generator.weekly_workload, ) - section_fitness_data, weekly_fitness_data = fitness_evaluator.evaluate_timetable_fitness() + ( + section_fitness_data, + weekly_fitness_data, + ) = fitness_evaluator.evaluate_timetable_fitness() # Save results with open("GA/chromosome.json", "w") as timetable_file: diff --git a/GA/mutation.py b/GA/mutation.py index 76156b6..9416ff4 100644 --- a/GA/mutation.py +++ b/GA/mutation.py @@ -5,84 +5,34 @@ class TimeTableMutation: - def __init__(self, mutation_rate = 0.7): - """ - Initializes the Mutation class with a specified mutation rate. - - Args: - mutation_rate (float): Fraction (0 to 1) of sections to mutate. - """ - + def __init__(self, mutation_rate=0.7): self.mutation_rate = mutation_rate - - def mutate_time_slots_in_section(self, schedule, section): - """ - Mutates the time slots within a particular section by shuffling them. - - Args: - schedule (dict): The schedule containing sections and their data. - section (str): The section in which mutation should occur. - - Returns: - bool: True if mutation was performed, False otherwise. - """ - - if section in schedule: - section_list = schedule[section] - - if len(section_list) < 2: # Not enough time slots to shuffle - return False - - time_slots = [entry["time_slot"] for entry in section_list] - random.shuffle(time_slots) - - for i, entry in enumerate(section_list): - entry["time_slot"] = time_slots[i] - - return True - return False - - - def mutate_schedule_for_week(self, weekly_schedule): - """ - Mutates the time slots for all weekdays in the weekly schedule. - - Args: - weekly_schedule (dict): The weekly schedule containing days, sections, and data. - - Returns: - dict: The mutated weekly schedule. - """ - - # Create a deep copy to keep the original intact - mutated_weekly_schedule = copy.deepcopy(weekly_schedule) - - for day, day_schedule in mutated_weekly_schedule.items(): - # Calculate the number of sections to mutate - total_sections = list(day_schedule.keys()) - num_to_mutate = max(1, int(self.mutation_rate * len(total_sections))) - - # Randomly select sections to mutate - sections_to_mutate = random.sample(total_sections, num_to_mutate) + def mutate_time_slots_in_section(self, schedule: dict, section: str) -> bool: + if section not in schedule or len(schedule[section]) < 2: + return False + + section_slots = schedule[section] + time_slots = [entry["time_slot"] for entry in section_slots] + random.shuffle(time_slots) + for i, entry in enumerate(section_slots): + entry["time_slot"] = time_slots[i] + return True + + def mutate_schedule_for_week(self, weekly_schedule: dict) -> dict: + mutated_schedule = copy.deepcopy(weekly_schedule) + for day, day_schedule in mutated_schedule.items(): + sections = list(day_schedule.keys()) + num_to_mutate = max(1, int(self.mutation_rate * len(sections))) + sections_to_mutate = random.sample(sections, num_to_mutate) for section in sections_to_mutate: self.mutate_time_slots_in_section(day_schedule, section) - - return mutated_weekly_schedule + return mutated_schedule class TimeTableCrossOver: - def perform_crossover(self, timetable1: dict, timetable2: dict): - """ - Crossovers two timetables. - Rule1: Swap the entire day TT. - """ - - crossover_days = Defaults.working_days - - # Perform the crossover between week1 and week2 - for day in crossover_days: - if day in (timetable1, timetable2): - timetable1[day], timetable2[day] = timetable1[day], timetable1[day] - + def perform_crossover(self, timetable1: dict, timetable2: dict) -> tuple: + for day in Defaults.working_days: + if day in timetable1 and day in timetable2: + timetable1[day], timetable2[day] = timetable2[day], timetable1[day] return timetable1, timetable2 diff --git a/GA/section_allocation.py b/GA/section_allocation.py deleted file mode 100644 index 4c866a2..0000000 --- a/GA/section_allocation.py +++ /dev/null @@ -1,164 +0,0 @@ -import random -from collections import defaultdict -from typing import List, Dict -from Constants.constant import SectionsConstants - - -class StudentScorer: - def __init__(self, attribute_weights: Dict[str, int] = None): - """ - Initialize the scorer with attribute weights. - - Defaults to SectionsConstants.ATTRIBUTE_WEIGHTS if not provided. - """ - - self.attribute_weights = attribute_weights or SectionsConstants.ATTRIBUTE_WEIGHTS - - - def calculate_dynamic_cgpa_threshold(self, students: List[Dict], top_percentage: int = 30) -> float: - """ - Calculate the CGPA threshold for the top X% of students. - - Args: - students (List[Dict]): List of student dictionaries with 'CGPA'. - top_percentage (int): Percentage of students considered top. - - Returns: - float: The CGPA threshold. - - """ - - sorted_cgpas = sorted( - (student['CGPA'] for student in students), - reverse=True - ) - - threshold_index = max(1, int(len(sorted_cgpas) * top_percentage / 100)) - 1 - return sorted_cgpas[threshold_index] - - - def assign_dynamic_conditions(self, cgpa_threshold: float): - """ - Assign a dynamic condition for CGPA based on the threshold. - - Args: - cgpa_threshold (float): The CGPA threshold for "good_cgpa". - """ - SectionsConstants.ATTRIBUTE_CONDITIONS['good_cgpa'] = lambda student: student['CGPA'] >= cgpa_threshold - - - def calculate_student_score(self, student: Dict) -> int: - """ - Calculate the score for a student based on attribute weights. - - Args: - student (Dict): A dictionary representing a student. - - Returns: - int: The student's score. - """ - - return sum( - weight - for attribute, weight in self.attribute_weights.items() - if SectionsConstants.ATTRIBUTE_CONDITIONS.get(attribute, lambda x: False)(student) - ) - - - def assign_scores_to_students(self, students: List[Dict]) -> List[Dict]: - """ - Assign scores to all students. - - Args: - students (List[Dict]): List of student dictionaries. - - Returns: - List[Dict]: The updated list of students with scores. - """ - - for student in students: - student['score'] = self.calculate_student_score(student) - return students - - - def divide_students_into_sections(self, students: List[Dict], class_strength: int) -> List[List[Dict]]: - """ - Divide students into sections based on their scores and class strength. - - Args: - students (List[Dict]): List of student dictionaries with scores. - class_strength (int): Maximum number of students per section. - - Returns: - List[List[Dict]]: List of sections containing students. - """ - grouped_by_score = defaultdict(list) - for student in students: - grouped_by_score[student['score']].append(student) - - sections = [] - current_section = [] - - for score_group in grouped_by_score.values(): - for student in score_group: - current_section.append(student) - if len(current_section) == class_strength: - sections.append(current_section) - current_section = [] - - if current_section: - sections.append(current_section) - - return sections - - - - - - -def generate_students(num_students: int = 500) -> List[Dict]: - """ - Generate a list of random students with CGPA and hostler status. - - Args: - num_students (int): Number of students to generate. - - Returns: - List[Dict]: List of student dictionaries. - """ - - return [ - { - 'ID': i, - 'CGPA': round(random.uniform(6.0, 9.8), 2), - 'Hostler': random.choice([True, False]) - } - for i in range(1, num_students + 1) - ] - - -if __name__ == "__main__": - # Initialize constants and scorer - scorer = StudentScorer() - students = generate_students(num_students=500) - - # Calculate the dynamic CGPA threshold - cgpa_threshold = scorer.calculate_dynamic_cgpa_threshold(students, top_percentage=30) - print(f"Dynamic CGPA Threshold (Top 30%): {cgpa_threshold}") - - # Assign conditions and scores - scorer.assign_dynamic_conditions(cgpa_threshold) - students_with_scores = scorer.assign_scores_to_students(students) - - # Divide students into sections - class_strength = 50 - sections = scorer.divide_students_into_sections(students_with_scores, class_strength) - - # Display the sections - for i, section in enumerate(sections, 1): - print(f"Section {i} (Total Students: {len(section)}):") - for student in section: - print( - f" Student ID: {student['ID']}, CGPA: {student['CGPA']}, " - f"Hostler: {student['Hostler']}, score: {student['score']}" - ) diff --git a/GA/selection.py b/GA/selection.py index ac254ea..b761136 100644 --- a/GA/selection.py +++ b/GA/selection.py @@ -5,40 +5,34 @@ class TimeTableSelection: def __init__(self): pass - def select_chromosomes( - self, - weekly_fitness_scores, - top_percentage=0.20, - roulette_percentage=0.10 + self, weekly_fitness_scores, top_percentage=0.20, roulette_percentage=0.10 ): """ - Entry point for selecting chromosomes. - Select chromosomes based on top scores and roulette wheel selection. + Entry point for selecting chromosomes. + Select chromosomes based on top scores and roulette-wheel selection. """ if not weekly_fitness_scores: print("Weekly fitness scores are empty. Ensure data is loaded correctly!") return {} - top_selected, remaining_scores = self.get_top_and_remaining_items(weekly_fitness_scores, top_percentage) + top_selected, remaining_scores = self.get_top_and_remaining_items( + weekly_fitness_scores, top_percentage + ) roulette_selected = self.roulette_wheel_selection( - remaining_scores, - int( - len(weekly_fitness_scores) * roulette_percentage - ) + remaining_scores, int(len(weekly_fitness_scores) * roulette_percentage) ) selected_fitness = {**top_selected, **roulette_selected} # self.display_selected_chromosomes(selected_fitness) return selected_fitness - @staticmethod def calculate_cumulative_probabilities(scores): """ - Calculate cumulative probabilities for roulette wheel selection. + Calculate cumulative probabilities for roulette wheel selection. """ cumulative_probabilities = [] @@ -50,11 +44,10 @@ def calculate_cumulative_probabilities(scores): return cumulative_probabilities - @staticmethod def perform_roulette_selection(cumulative_probabilities, total_fitness, num_select): """ - Perform roulette wheel selection to choose items. + Perform roulette-wheel selection to choose items. """ selected_items = [] @@ -67,10 +60,9 @@ def perform_roulette_selection(cumulative_probabilities, total_fitness, num_sele return selected_items - def roulette_wheel_selection(self, scores, num_select): """ - Select items using roulette wheel selection. + Select items using roulette-wheel selection. """ if not scores: @@ -79,39 +71,25 @@ def roulette_wheel_selection(self, scores, num_select): total_fitness = sum(scores.values()) cumulative_probabilities = self.calculate_cumulative_probabilities(scores) - selected_items = self.perform_roulette_selection(cumulative_probabilities, total_fitness, num_select) - return { - week: scores[week] for week in selected_items - } - + selected_items = self.perform_roulette_selection( + cumulative_probabilities, total_fitness, num_select + ) + return {week: scores[week] for week in selected_items} @staticmethod def get_top_and_remaining_items(scores, percentage): - """ - Select the top percentage of items based on scores and return the remaining items. - - Args: - scores (dict): A dictionary of items with their scores. - percentage (float): The percentage of top items to select. - - Returns: - tuple: A tuple of two dictionaries: - - Top-selected items and their scores. - - Remaining items and their scores. - """ - - # at least 1 chromosome to select. - num_select = max(1, int(len(scores) * percentage)) + num_select = max( + 1, int(len(scores) * percentage) + ) # at least 1 chromosome to select. sorted_scores = sorted(scores.items(), key=lambda x: x[1], reverse=True) top_selected = dict(sorted_scores[:num_select]) remaining_items = dict(sorted_scores[num_select:]) return top_selected, remaining_items - @staticmethod def display_selected_chromosomes(selected_fitness): """ - Display the selected chromosomes and their fitness scores. + Display the selected chromosomes and their fitness scores. """ print("\n--- Selected Weeks and Fitness Scores ---") diff --git a/IMPLEMENTATION_SUMMARY.md b/IMPLEMENTATION_SUMMARY.md new file mode 100644 index 0000000..5695371 --- /dev/null +++ b/IMPLEMENTATION_SUMMARY.md @@ -0,0 +1,169 @@ +# Implementation Summary + +## ✅ Completed Features + +### 1. **Detailed Timetable View** +- ✅ Full timetable display in college dashboard +- ✅ View shows complete schedule with days, time slots, sections, subjects, teachers, and classrooms +- ✅ Click "View" on any timetable card to see detailed schedule +- ✅ Route: `/college/timetables/:id` + +### 2. **Resource Management UI** +Complete CRUD interface for all resources: + +#### **Departments Tab** +- Add/Edit/Delete departments +- Fields: Name, Code + +#### **Teachers Tab** +- Add teachers with user account creation +- Fields: Name, Email, Password, Employee ID, Department, Designation, Max Weekly Hours +- Automatically creates user account with `faculty` role + +#### **Subjects Tab** +- Add/Edit/Delete subjects +- Fields: Code, Name, Credits, Weekly Quota, Department, Is Lab (checkbox) +- Visual indicator for Lab vs Theory subjects + +#### **Sections Tab** +- Add/Edit/Delete sections +- Fields: Name, Student Strength, Department, Year, Semester + +#### **Classrooms Tab** +- Add/Edit/Delete classrooms and labs +- Fields: Name, Capacity, Department, Is Lab (checkbox) +- Separate management for regular classrooms and labs + +#### **Subject-Teacher Mappings Tab** +- Create mappings between subjects and teachers +- Fields: Subject, Teacher, Section (optional), Primary Teacher (checkbox) +- Shows all current mappings in a table + +#### **Teacher Preferences Tab** +- Set preferred time slots for teachers +- Set preferred days +- Set unavailable time slots +- Visual checkbox interface for easy selection + +### 3. **Excel Export Functionality** +- ✅ Export timetable to Excel from detail view +- ✅ Export from timetable list (quick export) +- ✅ Creates properly formatted Excel file with: + - Days as rows + - Time slots as columns + - Sections as separate columns + - Subject, Teacher, Classroom in each cell +- ✅ Uses `xlsx` library (added to package.json) + +### 4. **Enhanced Generate Timetable Page** +- ✅ Shows resource status before generation +- ✅ Visual indicators (green checkmarks/red alerts) for each resource +- ✅ Prevents generation if required resources are missing +- ✅ Helpful error messages guiding user to add missing resources +- ✅ Link to Resources page for easy navigation + +### 5. **Improved Dashboard Navigation** +- ✅ Added "Resources" menu item +- ✅ Added "Dashboard" home page with statistics +- ✅ Better navigation structure +- ✅ Statistics cards showing counts of all resources + +### 6. **Backend Enhancements** +- ✅ Added `TeacherPreferenceViewSet` for managing preferences +- ✅ Added validation in generate endpoint to check for required resources +- ✅ Better error messages for missing resources +- ✅ All CRUD endpoints working for all resources + +## 📁 File Structure + +### Frontend Files Created/Updated: +``` +frontend/src/pages/college/ +├── Dashboard.jsx # Updated with Resources link and home page +├── Timetables.jsx # Updated with View button and Excel export +├── TimetableDetail.jsx # NEW - Detailed timetable view with Excel export +├── GenerateTimetable.jsx # Updated with resource status check +└── Resources.jsx # NEW - Complete resource management with tabs +``` + +### Backend Files Updated: +``` +backend/core/ +├── views.py # Added TeacherPreferenceViewSet, validation +└── urls.py # Added teacher-preferences endpoint +``` + +## 🎯 How to Use + +### For College Admin: + +1. **Setup Resources** (First Time): + - Go to "Resources" in navigation + - Add Departments + - Add Teachers (creates user accounts automatically) + - Add Subjects + - Add Sections + - Add Classrooms/Labs + - Create Subject-Teacher Mappings + - Set Teacher Preferences (optional but recommended) + +2. **Generate Timetable**: + - Go to "Generate" in navigation + - Check resource status (all should be green) + - Fill in timetable details + - Click "Generate Timetable" + - Wait for generation to complete + +3. **View Timetables**: + - Go to "Timetables" in navigation + - See all generated timetables + - Click "View" to see detailed schedule + - Click download icon for quick Excel export + +4. **Export to Excel**: + - From timetable list: Click download icon + - From detail view: Click "Export Excel" button + - File downloads automatically with proper formatting + +## 🔧 Installation Notes + +### Frontend Dependencies: +```bash +cd frontend +npm install +# xlsx package is already added to package.json +``` + +### Backend: +- All endpoints are already configured +- TeacherPreferenceViewSet is added +- Validation is in place + +## 📊 Resource Management Flow + +1. **Departments** → **Teachers** → **Subjects** → **Sections** → **Classrooms** +2. **Subject-Teacher Mappings** (links subjects to teachers) +3. **Teacher Preferences** (optional but improves results) +4. **Generate Timetable** (uses all above resources) + +## 🎨 UI Features + +- ✅ Tabbed interface for easy resource management +- ✅ Modal forms for adding/editing +- ✅ Visual status indicators +- ✅ Responsive design +- ✅ Client-friendly interface +- ✅ Clear error messages +- ✅ Loading states +- ✅ Empty states + +## 🚀 Next Steps (Optional Enhancements) + +1. Bulk import from Excel +2. Timetable editing after generation +3. Conflict detection and warnings +4. Teacher workload visualization +5. Room utilization reports +6. Print-friendly timetable view + +All core features are now implemented and ready for production use! diff --git a/README.md b/README.md index 96e14ac..ba7c659 100644 --- a/README.md +++ b/README.md @@ -1,116 +1,192 @@ -# Timetable Scheduling Project +# Timetable Management System + +A comprehensive, scalable timetable management system with multi-tenant support, genetic algorithm optimization, and modern React frontend. + +## Features + +- **Multi-Tenant Architecture**: Master admin can manage multiple colleges, each with their own dashboard +- **Genetic Algorithm**: Optimized timetable generation with constraint handling +- **Role-Based Access Control**: Master Admin, College Admin, HOD, Faculty, and Student roles +- **Modern Frontend**: React with TypeScript, Tailwind CSS, and responsive design +- **RESTful API**: Django REST Framework with JWT authentication +- **Scalable Design**: PostgreSQL database, Celery for async tasks, Redis for caching + +## Project Structure + +``` +Time-Table/ +├── backend/ # Django backend +│ ├── algorithm/ # Genetic algorithm implementation +│ ├── api/ # Authentication endpoints +│ ├── core/ # Core models, views, serializers +│ └── timetable_backend/ # Django settings +├── frontend/ # React frontend +│ └── src/ +│ ├── pages/ # Page components +│ ├── components/ # Reusable components +│ ├── stores/ # State management +│ └── lib/ # Utilities +└── README.md +``` + +## Setup Instructions + +### Backend Setup + +1. **Navigate to backend directory:** + ```bash + cd backend + ``` + +2. **Create virtual environment:** + ```bash + python -m venv venv + source venv/bin/activate # On Windows: venv\Scripts\activate + ``` + +3. **Install dependencies:** + ```bash + pip install -r requirements.txt + ``` + +4. **Set up environment variables:** + ```bash + cp .env.example .env + # Edit .env with your database credentials + ``` + +5. **Set up database:** + ```bash + python manage.py makemigrations core + python manage.py migrate + ``` + +6. **Create superuser (Master Admin):** + ```bash + python manage.py createsuperuser + ``` + +7. **Run server:** + ```bash + python manage.py runserver + ``` + +### Frontend Setup + +1. **Navigate to frontend directory:** + ```bash + cd frontend + ``` + +2. **Install dependencies:** + ```bash + npm install + ``` + +3. **Run development server:** + ```bash + npm run dev + ``` + +## Usage + +### Master Admin + +1. Login with master admin credentials +2. Navigate to Colleges section to add/manage colleges +3. Each college gets a unique subdomain and dashboard +4. Manage users across all colleges + +### College Admin + +1. Login with college admin credentials +2. Set up departments, teachers, subjects, sections, and classrooms +3. Generate timetables using the genetic algorithm +4. View and manage generated timetables + +## API Endpoints + +### Authentication +- `POST /api/auth/register/` - Register new user +- `POST /api/auth/login/` - Login +- `GET /api/auth/me/` - Get current user +- `POST /api/auth/logout/` - Logout + +### Colleges (Master Admin only) +- `GET /api/colleges/` - List colleges +- `POST /api/colleges/` - Create college +- `GET /api/colleges/{id}/` - Get college details +- `PATCH /api/colleges/{id}/` - Update college +- `DELETE /api/colleges/{id}/` - Delete college + +### Timetables +- `GET /api/timetables/` - List timetables +- `POST /api/timetables/` - Create timetable +- `GET /api/timetables/{id}/` - Get timetable details +- `POST /api/generate-timetable/generate/` - Generate timetable using GA + +## Algorithm + +The genetic algorithm optimizes timetable generation by: + +1. **Initialization**: Creates random timetable populations +2. **Fitness Evaluation**: Scores timetables based on constraint violations +3. **Selection**: Selects best timetables for reproduction +4. **Crossover**: Combines features from parent timetables +5. **Mutation**: Introduces random changes for diversity +6. **Iteration**: Repeats for specified generations + +### Constraints + +- **Hard Constraints**: Must be satisfied + - No teacher double-booking + - No classroom double-booking + - Room capacity limits + - Teacher availability + +- **Soft Constraints**: Preferences + - Teacher time preferences + - Balanced workload distribution + - Minimize gaps between classes + +## Sample Data + +To add sample data for testing: + +1. Use Django admin or API to create: + - Colleges + - Departments + - Teachers + - Subjects + - Sections + - Classrooms + - Subject-Teacher mappings + +2. Or use the management command (to be added): + ```bash + python manage.py load_sample_data + ``` + +## Deployment -## 📚 Overview -Welcome to the **Timetable Scheduling Project**! This repository documents research, findings, discussions, and progress on creating an efficient timetable scheduling system that addresses real-world challenges in academic scheduling. Our goal is to develop a scheduling model that optimally assigns rooms, teachers, and time slots while accommodating various constraints. +### Backend +- Use Gunicorn or uWSGI for production +- Set up PostgreSQL database +- Configure Redis for Celery +- Set environment variables securely + +### Frontend +- Build for production: `npm run build` +- Serve static files with Nginx or similar +- Configure API proxy -## 🔍 Project Goals -- **Optimal Assignment**: Effectively allocate rooms, teachers, and time slots. -- **Constraint Management**: Consider teacher preferences, student load, room capacities, and subject assignments. -- **Dynamic Adaptability**: Adjust to changes, such as adding new rooms or teachers, without complete model retraining. -- **Simplicity and Flexibility**: Simplify complex constraints while remaining adaptable to real-world needs. +## Contributing -## 🧬 Genetic Algorithm -The project employs a Genetic Algorithm (GA) to solve the scheduling problem, inspired by the principles of natural selection. Key modules in our GA include: +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Submit a pull request -1. **Data Encoding and Decoding**: Converting solutions into chromosomes for efficiency. -2. **Initial Population**: Generating a diverse set of potential solutions under specified constraints. -3. **Evaluation of Population**: Using a fitness function to assess solution quality. -4. **Crossover Evolution**: Creating new solutions by combining existing ones. -5. **Mutation**: Introducing random changes to explore new potential solutions. - -### Advantages -- Flexibility compared to manual systems -- Reduced processing power requirements -- Quick generation of accurate timetables -- User-friendly data entry and revision -- Improved productivity and minimal paperwork - -### Disadvantages -- Requires initial raw content input -- High memory consumption -- Internet connectivity needed - -## 📋 Applications -The genetic algorithm can efficiently create various timetables, including: -- University Timetable -- Exam Timetable -- School Timetable -- College Timetable -- Teacher Timetable - -## 📊 Results -The system generates distinct timetables for each class, faculty member, and lab, preventing slot conflicts and allowing customization. The program meets both hard and soft scheduling constraints. - -## ⚙️ Fitness Function -The fitness function is a critical component of our Genetic Algorithm, as it evaluates the quality of each timetable solution. It incorporates both hard and soft constraints to ensure a balanced and effective scheduling outcome. - -### Hard Constraints -These are strict requirements that must be satisfied for a solution to be considered valid: - -1. **No Overlap**: - - Teachers cannot be assigned to multiple classes at the same time. - - Time slots must not overlap for the same section. - - The same classroom cannot be allocated to multiple sections during the same time slot. - -2. **Room Capacity**: - - Each classroom's capacity must accommodate the number of students assigned to that class. - -3. **Time Slot Allocation**: - - Classes must be scheduled within designated time slots. - -4. **Teacher Load Distribution**: - - The total hours assigned to a teacher should not exceed their maximum allowable teaching hours. - -### Soft Constraints -These are preferences that enhance the quality of the solution but are not mandatory: - -1. **Teacher Preferences**: - - Assignments should consider teacher preferences for certain time slots or days. - -2. **Gaps Between Classes**: - - Minimizing the number of gaps between classes for teachers is preferred. - -3. **Balanced Workload**: - - Aim for a balanced distribution of classes among teachers to prevent overloading any single teacher. - -4. **Half Days for Sections** - - Every section should be provided half days so that resources can be utilised properly and also labs and calsses should be balanced. - -### Fitness Score Calculation -The fitness score is calculated based on the following criteria: - -- Each hard constraint violation deducts a significant penalty from the score (e.g., -100 points for each violation). -- Soft constraint preferences add to the score (e.g., +10 points for each preference met). -- The final score is a reflection of both the number of constraints satisfied and the overall quality of the timetable. - -A higher fitness score indicates a better solution, leading to more optimal timetables. The Genetic Algorithm iteratively evolves the population of solutions to maximize this fitness score over generations. - -## 🚀 Future Scope -This project aims to simplify faculty management by automatically assigning courses, considering workload balance, and tracking expertise. Future enhancements may include a master schedule for departments, integration with university websites, and personalized time slot assignments. - -## 🛠️ Installation - -To run this project, clone the repository, set up a virtual environment, and install the required dependencies: - -```bash -git clone https://github.com/yourusername/timetable-scheduling-project.git -cd timetable-scheduling-project - -# Set up a virtual environment -python -m venv venv - -# Activate the virtual environment -# For Linux/macOS: -source venv/bin/activate -# For Windows: -.\venv\Scripts\activate - -# Install dependencies -pip install -r requirements.txt - -# Optionally, set up environment variables in a .env file (e.g., DATABASE_URL, SECRET_KEY) -# Create and edit .env file with necessary variables -touch .env - -echo "Setup complete. You can now run the application." +## License +MIT License diff --git a/SETUP.md b/SETUP.md new file mode 100644 index 0000000..6e0ccb5 --- /dev/null +++ b/SETUP.md @@ -0,0 +1,116 @@ +# Setup Guide + +## Quick Start + +### Prerequisites +- Python 3.8+ +- Node.js 18+ +- PostgreSQL +- Redis (optional, for Celery) + +### Backend Setup + +1. **Navigate to backend:** + ```bash + cd backend + ``` + +2. **Create and activate virtual environment:** + ```bash + python -m venv venv + source venv/bin/activate # On Windows: venv\Scripts\activate + ``` + +3. **Install dependencies:** + ```bash + pip install -r requirements.txt + ``` + +4. **Configure environment:** + ```bash + cp .env.example .env + # Edit .env with your database credentials + ``` + +5. **Run migrations:** + ```bash + python manage.py makemigrations + python manage.py migrate + ``` + +6. **Create superuser:** + ```bash + python manage.py createsuperuser + # Or use the sample data command which creates one + ``` + +7. **Load sample data (optional):** + ```bash + python manage.py load_sample_data + ``` + +8. **Run server:** + ```bash + python manage.py runserver + ``` + +### Frontend Setup + +1. **Navigate to frontend:** + ```bash + cd frontend + ``` + +2. **Install dependencies:** + ```bash + npm install + ``` + +3. **Run development server:** + ```bash + npm run dev + ``` + +## Access the Application + +- Frontend: http://localhost:3000 +- Backend API: http://localhost:8000 +- API Documentation: http://localhost:8000/api/docs/ +- Django Admin: http://localhost:8000/admin/ + +## Default Credentials (after loading sample data) + +- **Master Admin:** + - Email: admin@timetable.com + - Password: admin123 + +- **College Admin:** + - Email: admin@democollege.com + - Password: admin123 + +- **Teachers:** + - Email: teacher1@demo.com, teacher2@demo.com, etc. + - Password: teacher123 + +## Troubleshooting + +### Database Connection Issues +- Ensure PostgreSQL is running +- Check database credentials in `.env` +- Verify database exists: `createdb timetable_db` + +### Frontend Build Issues +- Clear node_modules and reinstall: `rm -rf node_modules && npm install` +- Check Node.js version: `node --version` (should be 18+) + +### Import Errors +- Ensure all dependencies are installed +- Check Python virtual environment is activated +- Verify all migrations are applied + +## Next Steps + +1. Create your first college via Master Admin dashboard +2. Add departments, teachers, subjects, and sections +3. Generate your first timetable +4. Customize the algorithm parameters as needed diff --git a/Samples/Adhocs/common.py b/Samples/Adhocs/common.py new file mode 100644 index 0000000..c6e755b --- /dev/null +++ b/Samples/Adhocs/common.py @@ -0,0 +1,22 @@ +import random + + +def generate_students(num_students: int = 500) -> list: + """ + Generate a list of random students with CGPA and hostler status. + + Args: + num_students (int): Number of students to generate. + + Returns: + List[Dict]: List of student dictionaries. + """ + + return [ + { + "ID": i, + "CGPA": round(random.uniform(6.0, 9.8), 2), + "Hostler": random.choice([True, False]), + } + for i in range(1, num_students + 1) + ] diff --git a/Samples/Fitness_Files/LabConstraints.py b/Samples/Fitness_Files/LabConstraints.py new file mode 100644 index 0000000..7f25148 --- /dev/null +++ b/Samples/Fitness_Files/LabConstraints.py @@ -0,0 +1,143 @@ +import json +import random + +from constants.time_intervals import TimeIntervalConstant + + +class LabConstraintsConfig: + def __init__(self, config=None): + config = config or {} + + # Labs and Classes + self.LABS = config.get('labs', ["Lab1", "Lab2", "Lab3", "Lab4"]) + self.CLASSES = config.get('classes', ["Class1", "Class2", "Class3", "Class4"]) + self.TEACHERS = config.get('teachers', ["Teacher1", "Teacher2", "Teacher3", "Teacher4"]) + + # Constraints + self.MAX_LAB_COUNT = config.get('max_lab_count', 2) # Max labs per week for a teacher + self.MAX_CLASS_COUNT = config.get('max_class_count', 3) # Max classes per week for a teacher + self.MAX_TEACHER_WORKLOAD = config.get('max_teacher_workload', 5) # Max hours a teacher can work in a week + self.MAX_CONTINUOUS_CLASSES = config.get('max_continuous_classes', 2) # Max consecutive hours for a teacher + + # Workload for subjects + self.SUBJECT_WORKLOAD = config.get('subject_workload', { + "TCS-531": 3, + "TCS-502": 3, + "TCS-503": 3, + "PCS-506": 1, + "PCS-503": 1, + "TMA-502": 3, + "PMA-502": 1, + "TCS-509": 3, + "XCS-501": 2, + "CSP-501": 1, + "SCS-501": 1, + "Placement_Class": 1, + }) + + # Teacher availability and subjects they teach + self.TEACHER_SUBJECTS = config.get('teacher_subjects', { + "TCS-531": ["AB01", "PK02"], + "TCS-502": ["SS03", "AA04", "AC05"], + "TCS-503": ["SP06", "DP07", "AC05"], + "PCS-506": ["AD08", "RD09"], + "TMA-502": ["BJ10", "RS11", "JM12", "NJ13"], + "PMA-502": ["PM14", "AD08", "AA15"], + "TCS-509": ["SJ16", "AB17", "HP18", "SG19"], + "XCS-501": ["DT20", "PA21", "NB22"], + "CSP-501": ["AK23"], + "SCS-501": ["AP24"], + "PCS-503": ["RS11", "DP07", "SP06", "VD25"], + "Placement_Class": ["AK26"], + }) + +# Initialize available time slots +TimeSlots = TimeIntervalConstant.time_slots + + +class TimetableFitness: + def __init__(self, config=None): + self.config = LabConstraintsConfig(config) + self.teacher_schedule = {} + + def TeacherWorkload(self): + """ + Ensure total workload per teacher is within the limit. + """ + for teacher, schedule in self.teacher_schedule.items(): + total_hours = sum( + self.config.SUBJECT_WORKLOAD.get(subject, 0) for subject in schedule.values() + ) + if total_hours > self.config.MAX_TEACHER_WORKLOAD: + return False + return True + + def ContinuousClasses(self): + """ + Ensure teachers do not have more than the allowed continuous classes. + """ + for teacher, schedule in self.teacher_schedule.items(): + sorted_slots = sorted(schedule.keys()) + continuous_count = 0 + for i in range(len(sorted_slots) - 1): + if ( + TimeSlots.index(sorted_slots[i + 1]) + - TimeSlots.index(sorted_slots[i]) + == 1 + ): + continuous_count += 1 + if continuous_count > self.config.MAX_CONTINUOUS_CLASSES: + return False + else: + continuous_count = 0 + return True + + def fitnessFunc(self): + """ + Calculate fitness score based on all constraints. + """ + fitness_score = 0 + + if self.LabNo(): + fitness_score += 10 + else: + fitness_score -= 10 + + if self.ClassNo(): + fitness_score += 10 + else: + fitness_score -= 10 + + if self.TeacherWorkload(): + fitness_score += 10 + else: + fitness_score -= 10 + + if self.ContinuousClasses(): + fitness_score += 10 + else: + fitness_score -= 10 + + return fitness_score + + +# Main Execution +timetable_fitness = TimetableFitness() + +# Assign classes and labs to teachers +timetable_fitness.assign_classes_and_labs() + +# Calculate fitness score +fitness_score = timetable_fitness.fitnessFunc() + +# Output +print(f"Final Fitness Score: {fitness_score}") + +# Save results to a JSON file +output = { + "teacher_schedule": timetable_fitness.teacher_schedule, + "fitness_score": fitness_score, + "section_strength": timetable_fitness.section_strength, +} +with open("Timetable_Fitness_Result.json", "w") as f: + json.dump(output, f, indent=4) diff --git a/Samples/Fitness_Files/fitness.py b/Samples/Fitness_Files/fitness.py index fff0e31..347557a 100644 --- a/Samples/Fitness_Files/fitness.py +++ b/Samples/Fitness_Files/fitness.py @@ -1,11 +1,15 @@ import json + +from Constants.constant import Defaults, PenaltyConstants from Constants.time_intervals import TimeIntervalConstant from GA.chromosome import TimeTableGeneration -from Constants.constant import ( - PenaltyConstants, - Defaults, +from Samples.samples import ( + Classrooms, + SpecialSubjects, + SubjectTeacherMap, + SubjectWeeklyQuota, + TeacherWorkload, ) -from Samples.samples import SubjectTeacherMap, SpecialSubjects, SubjectWeeklyQuota, Classrooms, TeacherWorkload class TimetableFitnessEvaluator: @@ -59,16 +63,30 @@ def evaluate_timetable_fitness(self): section_strength = self.section_student_strength.get(section, 0) # Penalty 1: Double booking a teacher in the same time slot - if (assigned_teacher, assigned_time_slot) in teacher_time_slot_tracking.keys(): - section_fitness -= PenaltyConstants.PENALTY_TEACHER_DOUBLE_BOOKED + if ( + assigned_teacher, + assigned_time_slot, + ) in teacher_time_slot_tracking.keys(): + section_fitness -= ( + PenaltyConstants.PENALTY_TEACHER_DOUBLE_BOOKED + ) else: - teacher_time_slot_tracking[(assigned_teacher, assigned_time_slot)] = section + teacher_time_slot_tracking[ + (assigned_teacher, assigned_time_slot) + ] = section # Penalty 2: Double booking a classroom in the same time slot - if (assigned_classroom, assigned_time_slot) in classroom_time_slot_tracking: - section_fitness -= PenaltyConstants.PENALTY_CLASSROOM_DOUBLE_BOOKED + if ( + assigned_classroom, + assigned_time_slot, + ) in classroom_time_slot_tracking: + section_fitness -= ( + PenaltyConstants.PENALTY_CLASSROOM_DOUBLE_BOOKED + ) else: - classroom_time_slot_tracking[(assigned_classroom, assigned_time_slot)] = section + classroom_time_slot_tracking[ + (assigned_classroom, assigned_time_slot) + ] = section # Penalty 3: Over-capacity classrooms if section_strength > self.classroom_capacity.get( @@ -81,23 +99,33 @@ def evaluate_timetable_fitness(self): assigned_teacher, [] ) if assigned_time_slot not in preferred_time_slots: - section_fitness -= PenaltyConstants.PENALTY_UN_PREFERRED_SLOT + section_fitness -= ( + PenaltyConstants.PENALTY_UN_PREFERRED_SLOT + ) # Penalty 5: Scheduling teacher on a non-duty day assigned_day = day - if assigned_day not in TeacherWorkload.teacher_duty_days.get(assigned_teacher, []): + if assigned_day not in TeacherWorkload.teacher_duty_days.get( + assigned_teacher, [] + ): section_fitness -= PenaltyConstants.PENALTY_NON_DUTY_DAY # Tracking teacher workload if assigned_teacher not in teacher_workload_tracking: teacher_workload_tracking[assigned_teacher] = [] - teacher_workload_tracking[assigned_teacher].append(assigned_time_slot) + teacher_workload_tracking[assigned_teacher].append( + assigned_time_slot + ) # Penalty 6: Exceeding teacher daily workload for teacher, times_assigned in teacher_workload_tracking.items(): if teacher is not None: - if len(times_assigned) > self.teacher_daily_workload.get(teacher, 0): - section_fitness -= PenaltyConstants.PENALTY_OVERLOAD_TEACHER + if len(times_assigned) > self.teacher_daily_workload.get( + teacher, 0 + ): + section_fitness -= ( + PenaltyConstants.PENALTY_OVERLOAD_TEACHER + ) daily_section_fitness_scores[week][day][section] = section_fitness day_fitness += section_fitness @@ -143,7 +171,10 @@ def evaluate_timetable_fitness(self): timetable_generator.weekly_workload, ) - section_fitness_data, weekly_fitness_data = fitness_evaluator.evaluate_timetable_fitness() + ( + section_fitness_data, + weekly_fitness_data, + ) = fitness_evaluator.evaluate_timetable_fitness() # Save results with open("GA/chromosome.json", "w") as timetable_file: diff --git a/Samples/Mutation_Files/crossover.py b/Samples/Mutation_Files/crossover.py index 42841af..f505b68 100644 --- a/Samples/Mutation_Files/crossover.py +++ b/Samples/Mutation_Files/crossover.py @@ -3,48 +3,160 @@ def __init__(self): # schedules for week1 and week2 self.week1 = { "Monday": { - "A": [{"teacher_id": "SP06", "subject_id": "TCS-503", "classroom_id": "R1", "time_slot": "9:55 - 10:50"}], - "B": [{"teacher_id": "PM14", "subject_id": "PMA-502", "classroom_id": "L2", "time_slot": "9:00 - 9:55"}] + "A": [ + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + } + ], + "B": [ + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "9:00 - 9:55", + } + ], }, "Tuesday": { - "A": [{"teacher_id": "AK24", "subject_id": "CSP-501", "classroom_id": "R1", "time_slot": "9:00 - 9:55"}], - "B": [{"teacher_id": "BJ11", "subject_id": "TMA-502", "classroom_id": "R2", "time_slot": "9:55 - 10:50"}] + "A": [ + { + "teacher_id": "AK24", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + } + ], + "B": [ + { + "teacher_id": "BJ11", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + } + ], }, "Wednesday": { - "A": [{"teacher_id": "DT20", "subject_id": "XCS-501", "classroom_id": "R1", "time_slot": "11:10 - 12:05"}] + "A": [ + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05", + } + ] }, "Thursday": { - "A": [{"teacher_id": "SJ11", "subject_id": "TCS-509", "classroom_id": "R1", "time_slot": "12:05 - 1:00"}] + "A": [ + { + "teacher_id": "SJ11", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00", + } + ] }, "Friday": { - "A": [{"teacher_id": "AB01", "subject_id": "TCS-531", "classroom_id": "R3", "time_slot": "1:20 - 2:15"}] + "A": [ + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "1:20 - 2:15", + } + ] }, "Saturday": { - "A": [{"teacher_id": "SJ16", "subject_id": "TCS-509", "classroom_id": "R1", "time_slot": "2:15 - 3:10"}] - } + "A": [ + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10", + } + ] + }, } self.week2 = { "Monday": { - "A": [{"teacher_id": "SP06", "subject_id": "TCS-503", "classroom_id": "R1", "time_slot": "9:55 - 10:50"}], - "B": [{"teacher_id": "PM14", "subject_id": "PMA-502", "classroom_id": "L2", "time_slot": "9:00 - 9:55"}] + "A": [ + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + } + ], + "B": [ + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "9:00 - 9:55", + } + ], }, "Tuesday": { - "A": [{"teacher_id": "AK22", "subject_id": "CSP-501", "classroom_id": "R1", "time_slot": "9:00 - 9:55"}], - "B": [{"teacher_id": "BJ16", "subject_id": "TMA-502", "classroom_id": "R2", "time_slot": "9:55 - 10:50"}] + "A": [ + { + "teacher_id": "AK22", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + } + ], + "B": [ + { + "teacher_id": "BJ16", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + } + ], }, "Wednesday": { - "A": [{"teacher_id": "DT20", "subject_id": "XCS-501", "classroom_id": "R1", "time_slot": "11:10 - 12:05"}] + "A": [ + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05", + } + ] }, "Thursday": { - "A": [{"teacher_id": "SJ12", "subject_id": "TCS-509", "classroom_id": "R1", "time_slot": "12:05 - 1:00"}] + "A": [ + { + "teacher_id": "SJ12", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00", + } + ] }, "Friday": { - "A": [{"teacher_id": "AB01", "subject_id": "TCS-531", "classroom_id": "R3", "time_slot": "1:20 - 2:15"}] + "A": [ + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "1:20 - 2:15", + } + ] }, "Saturday": { - "A": [{"teacher_id": "SJ14", "subject_id": "TCS-509", "classroom_id": "R1", "time_slot": "2:15 - 3:10"}] - } + "A": [ + { + "teacher_id": "SJ14", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10", + } + ] + }, } self.new_offspring = None # Will hold the result after crossover @@ -58,11 +170,7 @@ def perform_crossover(self): self.week1[day], self.week2[day] = self.week2[day], self.week1[day] # Create a new chromosome structure with the updated schedules - self.new_offspring = { - "week1": self.week1, - "week2": self.week2 - - } + self.new_offspring = {"week1": self.week1, "week2": self.week2} def print_schedule(self): # Print the new chromosome after crossover @@ -76,4 +184,4 @@ def print_schedule(self): # Instantiate the class and run the crossover schedule_crossover = ScheduleCrossover() schedule_crossover.perform_crossover() -schedule_crossover.print_schedule() \ No newline at end of file +schedule_crossover.print_schedule() diff --git a/Samples/Mutation_Files/mutation.py b/Samples/Mutation_Files/mutation.py index 8082a14..48392ed 100644 --- a/Samples/Mutation_Files/mutation.py +++ b/Samples/Mutation_Files/mutation.py @@ -1,8 +1,9 @@ -import random import copy +import random + class Mutation: - def __init__(self, mutation_rate= random.random()): + def __init__(self, mutation_rate=random.random()): """ Initializes the Mutation class with a specified mutation rate. @@ -78,8 +79,10 @@ def print_weekly_schedule(self, weekly_schedule): for section, entries in day_schedule.items(): print(f" Section {section}:") for entry in entries: - print(f" Teacher: {entry['teacher_id']}, Subject: {entry['subject_id']}, " - f"Classroom: {entry['classroom_id']}, Time Slot: {entry['time_slot']}") + print( + f" Teacher: {entry['teacher_id']}, Subject: {entry['subject_id']}, " + f"Classroom: {entry['classroom_id']}, Time Slot: {entry['time_slot']}" + ) print("-" * 30) print("=" * 50) @@ -87,53 +90,205 @@ def print_weekly_schedule(self, weekly_schedule): # Example weekly schedule weekly_schedule = { "Monday": { - "A": [ - {"teacher_id": "AD08", "subject_id": "PCS-506", "classroom_id": "L5", "time_slot": "9:55 - 10:50"}, - {"teacher_id": "AD08", "subject_id": "PCS-506", "classroom_id": "L5", "time_slot": "9:00 - 9:55"}, - {"teacher_id": "AP24", "subject_id": "SCS-501", "classroom_id": "R1", "time_slot": "11:10 - 12:05"}, - {"teacher_id": "AK23", "subject_id": "CSP-501", "classroom_id": "R1", "time_slot": "12:05 - 1:00"}, - {"teacher_id": "SS03", "subject_id": "TCS-502", "classroom_id": "R1", "time_slot": "1:20 - 2:15"}, - {"teacher_id": "SP06", "subject_id": "TCS-503", "classroom_id": "R1", "time_slot": "2:15 - 3:10"}, - {"teacher_id": "DT20", "subject_id": "XCS-501", "classroom_id": "R1", "time_slot": "3:30 - 4:25"}, - ], - "B": [ - {"teacher_id": "PM14", "subject_id": "PMA-502", "classroom_id": "L2", "time_slot": "9:55 - 10:50"}, - {"teacher_id": "PM14", "subject_id": "PMA-502", "classroom_id": "L2", "time_slot": "9:00 - 9:55"}, - {"teacher_id": "BJ10", "subject_id": "TMA-502", "classroom_id": "R2", "time_slot": "11:10 - 12:05"}, - {"teacher_id": "PA21", "subject_id": "XCS-501", "classroom_id": "R2", "time_slot": "12:05 - 1:00"}, - ], - "C": [ - {"teacher_id": "RS11", "subject_id": "TMA-502", "classroom_id": "R3", "time_slot": "9:00 - 9:55"}, - {"teacher_id": "AA04", "subject_id": "TCS-502", "classroom_id": "R3", "time_slot": "9:55 - 10:50"}, - {"teacher_id": "RD09", "subject_id": "PCS-506", "classroom_id": "L3", "time_slot": "12:05 - 1:00"}, - {"teacher_id": "RD09", "subject_id": "PCS-506", "classroom_id": "L3", "time_slot": "11:10 - 12:05"}, - {"teacher_id": "DP07", "subject_id": "TCS-503", "classroom_id": "R3", "time_slot": "1:20 - 2:15"}, - {"teacher_id": "SJ16", "subject_id": "TCS-509", "classroom_id": "R3", "time_slot": "2:15 - 3:10"}, - {"teacher_id": "AB01", "subject_id": "TCS-531", "classroom_id": "R3", "time_slot": "3:30 - 4:25"}, - ], - "D": [ - {"teacher_id": "AD08", "subject_id": "PCS-506", "classroom_id": "L5", "time_slot": "9:55 - 10:50"}, - {"teacher_id": "AD08", "subject_id": "PCS-506", "classroom_id": "L5", "time_slot": "9:00 - 9:55"}, - {"teacher_id": "AC05", "subject_id": "TCS-502", "classroom_id": "R4", "time_slot": "11:10 - 12:05"}, - {"teacher_id": "PK02", "subject_id": "TCS-531", "classroom_id": "R4", "time_slot": "12:05 - 1:00"}, - ] + "A": [ + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "3:30 - 4:25", + }, + ], + "B": [ + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + ], + "C": [ + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25", + }, + ], + "D": [ + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + ], }, "Tuesday": { - "A": [{"teacher_id": "AK22", "subject_id": "CSP-501", "classroom_id": "R1", "time_slot": "9:00 - 9:55"}], - "B": [{"teacher_id": "BJ16", "subject_id": "TMA-502", "classroom_id": "R2", "time_slot": "9:55 - 10:50"}] + "A": [ + { + "teacher_id": "AK22", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + } + ], + "B": [ + { + "teacher_id": "BJ16", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + } + ], }, "Wednesday": { - "A": [{"teacher_id": "DT20", "subject_id": "XCS-501", "classroom_id": "R1", "time_slot": "11:10 - 12:05"}] + "A": [ + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05", + } + ] }, "Thursday": { - "A": [{"teacher_id": "SJ12", "subject_id": "TCS-509", "classroom_id": "R1", "time_slot": "12:05 - 1:00"}] + "A": [ + { + "teacher_id": "SJ12", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00", + } + ] }, "Friday": { - "A": [{"teacher_id": "AB01", "subject_id": "TCS-531", "classroom_id": "R3", "time_slot": "1:20 - 2:15"}] + "A": [ + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "1:20 - 2:15", + } + ] }, "Saturday": { - "A": [{"teacher_id": "SJ14", "subject_id": "TCS-509", "classroom_id": "R1", "time_slot": "2:15 - 3:10"}] - } + "A": [ + { + "teacher_id": "SJ14", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10", + } + ] + }, } # Create an instance of the Mutation class with a mutation rate randomly chosen @@ -148,4 +303,4 @@ def print_weekly_schedule(self, weekly_schedule): mutator.print_weekly_schedule(weekly_schedule) print("\nMutated Weekly Schedule:") -mutator.print_weekly_schedule(mutated_weekly_schedule) \ No newline at end of file +mutator.print_weekly_schedule(mutated_weekly_schedule) diff --git a/Samples/Mutation_Files/mutation2.py b/Samples/Mutation_Files/mutation2.py index 3295a22..d209396 100644 --- a/Samples/Mutation_Files/mutation2.py +++ b/Samples/Mutation_Files/mutation2.py @@ -1,5 +1,6 @@ import random + def mutate_time_slots_in_section(schedule, section): """ Mutates the time slots within a particular section by shuffling them. @@ -56,36 +57,146 @@ def mutate_all_sections(schedule): # Example schedule (reuse the previously defined schedule) schedule = { - "A": [ - {"teacher_id": "AD08", "subject_id": "PCS-506", "classroom_id": "L5", "time_slot": "9:55 - 10:50"}, - {"teacher_id": "AD08", "subject_id": "PCS-506", "classroom_id": "L5", "time_slot": "9:00 - 9:55"}, - {"teacher_id": "AP24", "subject_id": "SCS-501", "classroom_id": "R1", "time_slot": "11:10 - 12:05"}, - {"teacher_id": "AK23", "subject_id": "CSP-501", "classroom_id": "R1", "time_slot": "12:05 - 1:00"}, - {"teacher_id": "SS03", "subject_id": "TCS-502", "classroom_id": "R1", "time_slot": "1:20 - 2:15"}, - {"teacher_id": "SP06", "subject_id": "TCS-503", "classroom_id": "R1", "time_slot": "2:15 - 3:10"}, - {"teacher_id": "DT20", "subject_id": "XCS-501", "classroom_id": "R1", "time_slot": "3:30 - 4:25"}, - ], - "B": [ - {"teacher_id": "PM14", "subject_id": "PMA-502", "classroom_id": "L2", "time_slot": "9:55 - 10:50"}, - {"teacher_id": "PM14", "subject_id": "PMA-502", "classroom_id": "L2", "time_slot": "9:00 - 9:55"}, - {"teacher_id": "BJ10", "subject_id": "TMA-502", "classroom_id": "R2", "time_slot": "11:10 - 12:05"}, - {"teacher_id": "PA21", "subject_id": "XCS-501", "classroom_id": "R2", "time_slot": "12:05 - 1:00"}, - ], - "C": [ - {"teacher_id": "RS11", "subject_id": "TMA-502", "classroom_id": "R3", "time_slot": "9:00 - 9:55"}, - {"teacher_id": "AA04", "subject_id": "TCS-502", "classroom_id": "R3", "time_slot": "9:55 - 10:50"}, - {"teacher_id": "RD09", "subject_id": "PCS-506", "classroom_id": "L3", "time_slot": "12:05 - 1:00"}, - {"teacher_id": "RD09", "subject_id": "PCS-506", "classroom_id": "L3", "time_slot": "11:10 - 12:05"}, - {"teacher_id": "DP07", "subject_id": "TCS-503", "classroom_id": "R3", "time_slot": "1:20 - 2:15"}, - {"teacher_id": "SJ16", "subject_id": "TCS-509", "classroom_id": "R3", "time_slot": "2:15 - 3:10"}, - {"teacher_id": "AB01", "subject_id": "TCS-531", "classroom_id": "R3", "time_slot": "3:30 - 4:25"}, - ], - "D": [ - {"teacher_id": "AD08", "subject_id": "PCS-506", "classroom_id": "L5", "time_slot": "9:55 - 10:50"}, - {"teacher_id": "AD08", "subject_id": "PCS-506", "classroom_id": "L5", "time_slot": "9:00 - 9:55"}, - {"teacher_id": "AC05", "subject_id": "TCS-502", "classroom_id": "R4", "time_slot": "11:10 - 12:05"}, - {"teacher_id": "PK02", "subject_id": "TCS-531", "classroom_id": "R4", "time_slot": "12:05 - 1:00"}, - ], + "A": [ + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "3:30 - 4:25", + }, + ], + "B": [ + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L2", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + ], + "C": [ + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25", + }, + ], + "D": [ + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + ], } # Perform mutations on all sections diff --git a/Samples/Selection_Files/algo.py b/Samples/Selection_Files/algo.py index 5557e31..df320f8 100644 --- a/Samples/Selection_Files/algo.py +++ b/Samples/Selection_Files/algo.py @@ -1,24 +1,29 @@ -#ROULETTE WHEEL SELECTION +# ROULETTE WHEEL SELECTION """WORKING: Roulette Wheel Selection assigns sections of a "wheel" to each individual proportionally to their fitness. A spin of this wheel randomly selects an individual,giving higher fitness a larger slice of the wheel and, thus, a higher selection chance.""" def roulette_wheel_selection(self, chromosomes, selection_ratio=0.2): - fitness_scores = [(chromosome, self.calculate_fitness(chromosome)) for chromosome in chromosomes] + fitness_scores = [ + (chromosome, self.calculate_fitness(chromosome)) for chromosome in chromosomes + ] fitness_scores.sort(key=lambda x: x[1], reverse=True) selection_count = max(1, int(len(chromosomes) * selection_ratio)) top_chromosomes = [fitness_scores[i][0] for i in range(selection_count)] return top_chromosomes +# TOURNAMENT SELECTION +"""WORKINK: Tournament Selection randomly picks a small group from the population and selects the fittest from that group to pass + to the next generation. The "tournament size" controls how many individuals compete, balancing diversity and selection pressure.""" -#TOURNAMENT SELECTION -'''WORKINK: Tournament Selection randomly picks a small group from the population and selects the fittest from that group to pass - to the next generation. The "tournament size" controls how many individuals compete, balancing diversity and selection pressure.''' - -def select_top_20_percent_tournament(self, chromosomes, tournament_size=3, selection_ratio=0.2): - fitness_scores = [(chromosome, self.calculate_fitness(chromosome)) for chromosome in chromosomes] +def select_top_20_percent_tournament( + self, chromosomes, tournament_size=3, selection_ratio=0.2 +): + fitness_scores = [ + (chromosome, self.calculate_fitness(chromosome)) for chromosome in chromosomes + ] fitness_scores.sort(key=lambda x: x[1], reverse=True) selected_chromosomes = [] num_selected = int(len(chromosomes) * selection_ratio) @@ -30,14 +35,15 @@ def select_top_20_percent_tournament(self, chromosomes, tournament_size=3, selec return selected_chromosomes - -#STOCHASTIC UNIVERSAL SAMPLING (SUS) -'''WORKING: In SUS, multiple equally spaced pointers are placed on a roulette-style wheel, selecting individuals at each pointer - position.This method achieves a more balanced selection by ensuring broader population representation each generation.''' +# STOCHASTIC UNIVERSAL SAMPLING (SUS) +"""WORKING: In SUS, multiple equally spaced pointers are placed on a roulette-style wheel, selecting individuals at each pointer + position.This method achieves a more balanced selection by ensuring broader population representation each generation.""" def select_top_20_percent_sus(self, chromosomes, selection_ratio=0.2): - fitness_scores = [(chromosome, self.calculate_fitness(chromosome)) for chromosome in chromosomes] + fitness_scores = [ + (chromosome, self.calculate_fitness(chromosome)) for chromosome in chromosomes + ] fitness_scores.sort(key=lambda x: x[1], reverse=True) total_fitness = sum(score for _, score in fitness_scores) @@ -55,20 +61,21 @@ def select_top_20_percent_sus(self, chromosomes, selection_ratio=0.2): return selected_chromosomes - -#RANK SELECTION -'''WORKING: Rank Selection orders individuals by fitness, assigning higher ranks (not raw scores) better selection chances. This controls selection pressure, +# RANK SELECTION +"""WORKING: Rank Selection orders individuals by fitness, assigning higher ranks (not raw scores) better selection chances. This controls selection pressure, reducing the likelihood of overly fit individuals dominating too early.Rank Selection orders individuals by fitness, assigning higher ranks - (not raw scores) better selection chances. This controls selection pressure, reducing the likelihood of overly fit individuals dominating too early.''' + (not raw scores) better selection chances. This controls selection pressure, reducing the likelihood of overly fit individuals dominating too early.""" def select_top_20_percent_rank(self, chromosomes, selection_ratio=0.2): - fitness_scores = [(chromosome, self.calculate_fitness(chromosome)) for chromosome in chromosomes] + fitness_scores = [ + (chromosome, self.calculate_fitness(chromosome)) for chromosome in chromosomes + ] fitness_scores.sort(key=lambda x: x[1], reverse=True) ranked_chromosomes = [chromosome for chromosome, _ in fitness_scores] total_chromosomes = len(ranked_chromosomes) num_selected = int(total_chromosomes * selection_ratio) - + selected_chromosomes = [] for i in range(num_selected): selected_chromosomes.append(ranked_chromosomes[i]) @@ -76,31 +83,30 @@ def select_top_20_percent_rank(self, chromosomes, selection_ratio=0.2): return selected_chromosomes - -#RANDOM SELECTION -'''WORKING: Random Selection picks individuals purely at random, disregarding fitness. This maximizes diversity but lacks directional -pressure, often used in early generations or in combination with other methods.''' +# RANDOM SELECTION +"""WORKING: Random Selection picks individuals purely at random, disregarding fitness. This maximizes diversity but lacks directional +pressure, often used in early generations or in combination with other methods.""" def select_random(self, chromosomes, selection_ratio=0.2): num_chromosomes = len(chromosomes) num_selected = int(num_chromosomes * selection_ratio) - + selected_chromosomes = random.sample(chromosomes, num_selected) return selected_chromosomes - -#ELITISM SELECTION -'''WORKING: Elitism Selection ensures the top few individuals automatically pass to the next generation. This protects the best solutions -and accelerates convergence, as the most fit solutions are retained directly.''' +# ELITISM SELECTION +"""WORKING: Elitism Selection ensures the top few individuals automatically pass to the next generation. This protects the best solutions +and accelerates convergence, as the most fit solutions are retained directly.""" def select_elitism(self, chromosomes, fitness_scores, elitism_ratio=0.2): num_chromosomes = len(chromosomes) num_selected = int(num_chromosomes * elitism_ratio) - - sorted_indices = sorted(range(num_chromosomes), key=lambda i: fitness_scores[i], reverse=True) + + sorted_indices = sorted( + range(num_chromosomes), key=lambda i: fitness_scores[i], reverse=True + ) elite_chromosomes = [chromosomes[i] for i in sorted_indices[:num_selected]] return elite_chromosomes - diff --git a/Samples/Selection_Files/selection.py b/Samples/Selection_Files/selection.py index 006b4bc..94a9a5b 100644 --- a/Samples/Selection_Files/selection.py +++ b/Samples/Selection_Files/selection.py @@ -1,9 +1,20 @@ import random + class TimeIntervalConstant: @staticmethod def get_all_time_slots(): - return ["9:00-10:00", "10:00-11:00", "11:00-12:00", "12:00-1:00", "1:00-2:00", "2:00-3:00", "3:00-4:00", "4:00-5:00"] + return [ + "9:00-10:00", + "10:00-11:00", + "11:00-12:00", + "12:00-1:00", + "1:00-2:00", + "2:00-3:00", + "3:00-4:00", + "4:00-5:00", + ] + class Timetable: def __init__(self): @@ -18,7 +29,7 @@ def __init__(self): "T2": ["S2", "S4", "S6"], "T3": ["S2", "S5"], "T4": ["S7", "S6"], - "T5": ["S5", "S1"] + "T5": ["S5", "S1"], } self.teacher_max_hours = {"T1": 4, "T2": 5, "T3": 4, "T4": 3, "T5": 4} self.room_capacity = {"R1": 30, "R2": 25, "R3": 20} @@ -28,7 +39,7 @@ def generate_day_schedule(self): day_schedule = {} time_slot_classroom_usage = {time_slot: set() for time_slot in self.time_slots} time_slot_teacher_usage = {time_slot: set() for time_slot in self.time_slots} - + for section in self.sections: section_schedule = [] for time_slot in self.time_slots: @@ -37,7 +48,7 @@ def generate_day_schedule(self): "teacher_id": "None", "subject_id": "Break", "classroom_id": "N/A", - "time_slot": time_slot + "time_slot": time_slot, } else: teacher, subject, classroom = None, None, None @@ -46,8 +57,10 @@ def generate_day_schedule(self): teacher = random.choice(self.teachers) subject = random.choice(self.teacher_subject_map[teacher]) classroom = random.choice(self.classrooms) - if teacher not in time_slot_teacher_usage[time_slot] and \ - classroom not in time_slot_classroom_usage[time_slot]: + if ( + teacher not in time_slot_teacher_usage[time_slot] + and classroom not in time_slot_classroom_usage[time_slot] + ): break attempts += 1 @@ -58,7 +71,7 @@ def generate_day_schedule(self): "teacher_id": teacher, "subject_id": subject, "classroom_id": classroom, - "time_slot": time_slot + "time_slot": time_slot, } section_schedule.append(schedule_item) day_schedule[section] = section_schedule @@ -83,9 +96,9 @@ def calculate_fitness(self, chromosome): teacher_load = {} for item in section_schedule: - teacher = item['teacher_id'] - classroom = item['classroom_id'] - time_slot = item['time_slot'] + teacher = item["teacher_id"] + classroom = item["classroom_id"] + time_slot = item["time_slot"] strength = self.section_strength[section] if "Break" in time_slot: @@ -123,38 +136,49 @@ def calculate_fitness(self, chromosome): def select_top_chromosomes(self, population: list, percentage=0.30) -> list: """ Selects the top chromosomes based on their fitness scores from the population. - + :param population: List of chromosomes (each chromosome is a dictionary representing a timetable). :param percentage: The proportion of chromosomes to select (default is 30%). :return: A list of selected chromosomes. """ num_to_select = int(len(population) * percentage) - fitness_scores = [(chromosome, self.calculate_fitness(chromosome)) for chromosome in population] + fitness_scores = [ + (chromosome, self.calculate_fitness(chromosome)) + for chromosome in population + ] sorted_chromosomes = sorted(fitness_scores, key=lambda x: x[1], reverse=True) - + best_num = int(num_to_select * 0.20) worst_num = int(num_to_select * 0.10) middle_num = num_to_select - (best_num + worst_num) - + best_chromosomes = sorted_chromosomes[:best_num] worst_chromosomes = sorted_chromosomes[-worst_num:] - middle_chromosomes = sorted_chromosomes[best_num:best_num + middle_num] - + middle_chromosomes = sorted_chromosomes[best_num : best_num + middle_num] + roulette_num = int(middle_num * 0.70) rank_num = middle_num - roulette_num - + total_fitness = sum(fitness for _, fitness in middle_chromosomes) - roulette_chromosomes = random.choices(middle_chromosomes, weights=[fitness / total_fitness for _, fitness in middle_chromosomes], k=roulette_num) - + roulette_chromosomes = random.choices( + middle_chromosomes, + weights=[fitness / total_fitness for _, fitness in middle_chromosomes], + k=roulette_num, + ) + total_rank = sum(range(1, len(middle_chromosomes) + 1)) - rank_probabilities = [i / total_rank for i in range(1, len(middle_chromosomes) + 1)] - rank_chromosomes = random.choices(middle_chromosomes, weights=rank_probabilities, k=rank_num) - + rank_probabilities = [ + i / total_rank for i in range(1, len(middle_chromosomes) + 1) + ] + rank_chromosomes = random.choices( + middle_chromosomes, weights=rank_probabilities, k=rank_num + ) + selected_chromosomes = [chromosome for chromosome, _ in best_chromosomes] selected_chromosomes += [chromosome for chromosome, _ in worst_chromosomes] selected_chromosomes += [chromosome for chromosome, _ in roulette_chromosomes] selected_chromosomes += [chromosome for chromosome, _ in rank_chromosomes] - + # Output the breakdown print(f"Total Number of Chromosomes: {len(population)}") print(f"30% of Total Chromosomes: {num_to_select}") @@ -162,15 +186,20 @@ def select_top_chromosomes(self, population: list, percentage=0.30) -> list: print(f"Number of Worst Chromosomes: {worst_num}") print(f"Number of Chromosomes for Roulette: {roulette_num}") print(f"Number of Chromosomes for Rank: {rank_num}") - + return selected_chromosomes + # Example usage timetable_obj = Timetable() num_chromosomes = 1000 chromosomes = timetable_obj.create_multiple_timelines(num_chromosomes) -selected_chromosomes = timetable_obj.select_top_chromosomes(chromosomes, percentage=0.30) +selected_chromosomes = timetable_obj.select_top_chromosomes( + chromosomes, percentage=0.30 +) # Output selected chromosomes -for chromosome in selected_chromosomes[:5]: # Displaying the first 5 selected chromosomes +for chromosome in selected_chromosomes[ + :5 +]: # Displaying the first 5 selected chromosomes print(chromosome) diff --git a/test-suite/__init__.py b/Samples/__init__.py similarity index 100% rename from test-suite/__init__.py rename to Samples/__init__.py diff --git a/Samples/sample_chromosome.json b/Samples/sample_chromosome.json new file mode 100644 index 0000000..e29e586 --- /dev/null +++ b/Samples/sample_chromosome.json @@ -0,0 +1,1470 @@ +{ + "timetable": { + "Week 1": { + "Monday": { + "A": [ + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R1", + "time_slot": "3:30 - 4:25" + } + ], + "B": [ + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L4", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L4", + "time_slot": "11:10 - 12:05" + } + ], + "C": [ + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" + } + ], + "D": [ + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "SG19", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" + } + ] + }, + "Tuesday": { + "A": [ + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "11:10 - 12:05" + } + ], + "B": [ + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "3:30 - 4:25" + } + ], + "C": [ + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "NB22", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25" + } + ], + "D": [ + { + "teacher_id": "SG19", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" + } + ] + }, + "Wednesday": { + "A": [ + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L5", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L5", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "3:30 - 4:25" + } + ], + "B": [ + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" + } + ], + "C": [ + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "L2", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "L2", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25" + } + ], + "D": [ + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" + } + ] + }, + "Thursday": { + "A": [ + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00" + } + ], + "B": [ + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "3:30 - 4:25" + } + ], + "C": [ + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L4", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L4", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "L5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "L5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25" + } + ], + "D": [ + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" + } + ] + }, + "Friday": { + "A": [ + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00" + } + ], + "B": [ + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "3:30 - 4:25" + } + ], + "C": [ + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25" + } + ], + "D": [ + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" + } + ] + } + }, + "Week 2": { + "Monday": { + "A": [ + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "3:30 - 4:25" + } + ], + "B": [ + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "3:30 - 4:25" + } + ], + "C": [ + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L4", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L4", + "time_slot": "11:10 - 12:05" + } + ], + "D": [ + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "NB22", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" + } + ] + }, + "Tuesday": { + "A": [ + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AK26", + "subject_id": "Placement_Class", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "3:30 - 4:25" + } + ], + "B": [ + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" + } + ], + "C": [ + { + "teacher_id": "NB22", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R3", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25" + } + ], + "D": [ + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" + } + ] + }, + "Wednesday": { + "A": [ + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "11:10 - 12:05" + } + ], + "B": [ + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" + } + ], + "C": [ + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25" + } + ], + "D": [ + { + "teacher_id": "SP06", + "subject_id": "PCS-503", + "classroom_id": "L4", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "SP06", + "subject_id": "PCS-503", + "classroom_id": "L4", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "NB22", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25" + } + ] + }, + "Thursday": { + "A": [ + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "3:30 - 4:25" + } + ], + "B": [ + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" + } + ], + "C": [ + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25" + } + ], + "D": [ + { + "teacher_id": "NJ13", + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" + } + ] + }, + "Friday": { + "A": [ + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00" + } + ], + "B": [ + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "3:30 - 4:25" + } + ], + "C": [ + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "2:15 - 3:10" + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "1:20 - 2:15" + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25" + } + ], + "D": [ + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L3", + "time_slot": "9:55 - 10:50" + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L3", + "time_slot": "9:00 - 9:55" + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05" + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00" + } + ] + } + } + }, + "week_fitness_scores": [ + { + "week": "Week 1", + "score": 800 + }, + { + "week": "Week 2", + "score": 750 + } + ], + "section_fitness_scores": { + "Monday": { + "A": 15, + "B": 15, + "C": -20, + "D": 30 + }, + "Tuesday": { + "A": 65, + "B": 80, + "C": 65, + "D": 30 + }, + "Wednesday": { + "A": 30, + "B": 80, + "C": 15, + "D": 15 + }, + "Thursday": { + "A": 15, + "B": 80, + "C": 15, + "D": 80 + }, + "Friday": { + "A": 80, + "B": 15, + "C": 15, + "D": 30 + } + } +} \ No newline at end of file diff --git a/Samples/samples.py b/Samples/samples.py index f4c1b39..ae5227a 100644 --- a/Samples/samples.py +++ b/Samples/samples.py @@ -1,3 +1,15 @@ +class TimeSlots: + time_slots = { + 1: "9:00 - 9:55", + 2: "9:55 - 10:50", + 3: "11:10 - 12:05", + 4: "12:05 - 1:00", + 5: "1:20 - 2:15", + 6: "2:15 - 3:10", + 7: "3:30 - 4:25", + } + + class SubjectTeacherMap: subject_teacher_map = { "TCS-531": ["AB01", "PK02"], @@ -12,32 +24,22 @@ class SubjectTeacherMap: "SCS-501": ["AP24"], "PCS-503": ["RS11", "DP07", "SP06", "VD25"], "Placement_Class": ["AK26"], - "TCS-511":["PK02"], - "TCS-592":["AB01"], - "TCS-512":["HP18"], - "TCS-519":["DP07"], - "PCS-512":["VD25"] - + "TCS-511": ["PK02"], + "TCS-592": ["AB01"], + "TCS-512": ["HP18"], + "TCS-519": ["DP07"], + "PCS-512": ["VD25"], } -class WorkingDays: - days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] -class Sections: - sections = [] - - def __init__(self, section_number): - self.sections = [chr(65 + i) for i in range(section_number)] - - -class Classrooms: - classrooms = ["R1", "R2", "R3", "R4", "R5"] - labs = ["L1", "L2", "L3", "L4", "L5"] +class WorkingDays: + days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] class RoomCapacity: room_capacity = {"R1": 200, "R2": 230, "R3": 240, "R4": 250, "R5": 250} - section_strength = {"A": 200, "B": 200, "C": 200, "D": 100} + section_strength = {"A": 70, "B": 100, "C": 75, "D": 100} + lab_capacity = {"L1": 70, "L2": 50, "L3": 70, "L4": 50, "L5": 70, "L6": 50} class SubjectWeeklyQuota: @@ -59,7 +61,7 @@ class SubjectWeeklyQuota: class SpecialSubjects: special_subjects = ["Placement_Class"] - Labs=["PCS-506", "PCS-503", "PMA-502"] + Labs = ["PCS-506", "PCS-503", "PMA-502"] class PenaltyConstants: @@ -97,7 +99,7 @@ class TeacherWorkload: "AK23": 5, "AP24": 5, "VD25": 5, - "AK26": 5 + "AK26": 5, } teacher_preferences = { @@ -130,10 +132,10 @@ class TeacherWorkload: } teacher_duty_days = { "AB01": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], - "PK02": ["Tuesday", "Wednesday", "Thursday", "Friday","Saturday"], + "PK02": ["Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], "SS03": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], "AA04": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], - "AC05": ["Tuesday", "Wednesday", "Thursday", "Friday","Saturday"], + "AC05": ["Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], "SP06": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], "DP07": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], "AD08": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], @@ -147,1439 +149,1673 @@ class TeacherWorkload: "SJ16": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], "AB17": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], "HP18": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], - "SG19": ["Tuesday", "Wednesday", "Thursday", "Friday","Saturday"], + "SG19": ["Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], "DT20": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], "PA21": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], - "NB22": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday","Saturday"], + "NB22": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], "AK23": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], - "AP24": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday","Saturday"], - "VD25": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday","Saturday"], - "AK26": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday","Saturday"], + "AP24": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], + "VD25": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], + "AK26": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], } + + +class InterDepartment: + teacher_availability_matrix = { + "AB01": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "PK02": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "SS03": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "AA04": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "AC05": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "SP06": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "DP07": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "AD08": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "RD09": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "BJ10": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "RS11": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "JM12": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "NJ13": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "PM14": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "AA15": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "SJ16": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "AB17": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "HP18": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "SG19": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "DT20": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "PA21": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "NB22": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "AK23": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "AP24": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "VD25": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "AK26": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + } + + +class Lab_availability: + lab_availability_matrix = { + "L1": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "L2": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "L3": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "L4": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "L5": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "L6": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + } + + class SampleChromosome: - schedule1={ - "Monday": { - "A": [ - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R1", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "SP06", - "subject_id": "TCS-503", - "classroom_id": "R1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R1", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "SS03", - "subject_id": "TCS-502", - "classroom_id": "R1", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "BJ10", - "subject_id": "TMA-502", - "classroom_id": "R1", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R1", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R1", - "time_slot": "3:30 - 4:25" - } - ], - "B": [ - { - "teacher_id": "RS11", - "subject_id": "TMA-502", - "classroom_id": "R2", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AB17", - "subject_id": "TCS-509", - "classroom_id": "R2", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L4", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L4", - "time_slot": "11:10 - 12:05" - } - ], - "C": [ - { - "teacher_id": "DP07", - "subject_id": "TCS-503", - "classroom_id": "R3", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "HP18", - "subject_id": "TCS-509", - "classroom_id": "R3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R3", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "PK02", - "subject_id": "TCS-531", - "classroom_id": "R3", - "time_slot": "12:05 - 1:00" - } - ], - "D": [ - { - "teacher_id": "AA04", - "subject_id": "TCS-502", - "classroom_id": "R4", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AC05", - "subject_id": "TCS-503", - "classroom_id": "R4", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R4", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "SG19", - "subject_id": "TCS-509", - "classroom_id": "R4", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R4", - "time_slot": "3:30 - 4:25" - } - ] - }, - "Tuesday": { - "A": [ - { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R1", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "11:10 - 12:05" - } - ], - "B": [ - { - "teacher_id": "SS03", - "subject_id": "TCS-502", - "classroom_id": "R2", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R2", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AB17", - "subject_id": "TCS-509", - "classroom_id": "R2", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R2", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "BJ10", - "subject_id": "TMA-502", - "classroom_id": "R2", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R2", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "PA21", - "subject_id": "XCS-501", - "classroom_id": "R2", - "time_slot": "3:30 - 4:25" - } - ], - "C": [ - { - "teacher_id": "AA04", - "subject_id": "TCS-502", - "classroom_id": "R3", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "NB22", - "subject_id": "XCS-501", - "classroom_id": "R3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L2", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "PK02", - "subject_id": "TCS-531", - "classroom_id": "R3", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "SP06", - "subject_id": "TCS-503", - "classroom_id": "R3", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "HP18", - "subject_id": "TCS-509", - "classroom_id": "R3", - "time_slot": "3:30 - 4:25" - } - ], - "D": [ - { - "teacher_id": "SG19", - "subject_id": "TCS-509", - "classroom_id": "R4", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R4", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R4", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R4", - "time_slot": "12:05 - 1:00" - } - ] - }, - "Wednesday": { - "A": [ - { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R1", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L5", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L5", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "SP06", - "subject_id": "TCS-503", - "classroom_id": "R1", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "BJ10", - "subject_id": "TMA-502", - "classroom_id": "R1", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R1", - "time_slot": "3:30 - 4:25" - } - ], - "B": [ - { - "teacher_id": "AB17", - "subject_id": "TCS-509", - "classroom_id": "R2", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "SS03", - "subject_id": "TCS-502", - "classroom_id": "R2", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "RS11", - "subject_id": "TMA-502", - "classroom_id": "R2", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R2", - "time_slot": "12:05 - 1:00" - } - ], - "C": [ - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L3", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R3", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R3", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "DP07", - "subject_id": "TCS-503", - "classroom_id": "L2", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "DP07", - "subject_id": "TCS-503", - "classroom_id": "L2", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R3", - "time_slot": "3:30 - 4:25" - } - ], - "D": [ - { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L1", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AC05", - "subject_id": "TCS-503", - "classroom_id": "R4", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "JM12", - "subject_id": "TMA-502", - "classroom_id": "R4", - "time_slot": "12:05 - 1:00" - } - ] - }, - "Thursday": { - "A": [ - { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R1", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "SP06", - "subject_id": "TCS-503", - "classroom_id": "R1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R1", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "BJ10", - "subject_id": "TMA-502", - "classroom_id": "R1", - "time_slot": "12:05 - 1:00" - } - ], - "B": [ - { - "teacher_id": "DP07", - "subject_id": "TCS-503", - "classroom_id": "R2", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "SS03", - "subject_id": "TCS-502", - "classroom_id": "R2", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R2", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "PA21", - "subject_id": "XCS-501", - "classroom_id": "R2", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "RS11", - "subject_id": "TMA-502", - "classroom_id": "R2", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R2", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R2", - "time_slot": "3:30 - 4:25" - } - ], - "C": [ - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L4", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L4", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "L5", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "L5", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R3", - "time_slot": "3:30 - 4:25" - } - ], - "D": [ - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R4", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "PK02", - "subject_id": "TCS-531", - "classroom_id": "R4", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "JM12", - "subject_id": "TMA-502", - "classroom_id": "R4", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AB17", - "subject_id": "TCS-509", - "classroom_id": "R4", - "time_slot": "12:05 - 1:00" - } - ] - }, - "Friday": { - "A": [ - { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R1", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "SS03", - "subject_id": "TCS-502", - "classroom_id": "R1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R1", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R1", - "time_slot": "12:05 - 1:00" - } - ], - "B": [ - { - "teacher_id": "BJ10", - "subject_id": "TMA-502", - "classroom_id": "R2", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R2", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R2", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AA04", - "subject_id": "TCS-502", - "classroom_id": "R2", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R2", - "time_slot": "3:30 - 4:25" - } - ], - "C": [ - { - "teacher_id": "RS11", - "subject_id": "TMA-502", - "classroom_id": "R3", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AC05", - "subject_id": "TCS-502", - "classroom_id": "R3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R3", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "SP06", - "subject_id": "TCS-503", - "classroom_id": "R3", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L5", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L5", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "AB17", - "subject_id": "TCS-509", - "classroom_id": "R3", - "time_slot": "3:30 - 4:25" - } - ], - "D": [ - { - "teacher_id": "SS03", - "subject_id": "TCS-502", - "classroom_id": "R4", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R4", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R4", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "HP18", - "subject_id": "TCS-509", - "classroom_id": "R4", - "time_slot": "12:05 - 1:00" - } - ] - } - } - schedule2={ - "Monday": { - "A": [ - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R1", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L5", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L5", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R1", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R1", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "SS03", - "subject_id": "TCS-502", - "classroom_id": "R1", - "time_slot": "3:30 - 4:25" - } - ], - "B": [ - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R2", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AB17", - "subject_id": "TCS-509", - "classroom_id": "R2", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "SP06", - "subject_id": "TCS-503", - "classroom_id": "R2", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R2", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "PA21", - "subject_id": "XCS-501", - "classroom_id": "R2", - "time_slot": "3:30 - 4:25" - } - ], - "C": [ - { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L5", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "RD09", - "subject_id": "PCS-506", - "classroom_id": "L5", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L4", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L4", - "time_slot": "11:10 - 12:05" - } - ], - "D": [ - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L1", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "NB22", - "subject_id": "XCS-501", - "classroom_id": "R4", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "DP07", - "subject_id": "TCS-503", - "classroom_id": "R4", - "time_slot": "12:05 - 1:00" - } - ] - }, - "Tuesday": { - "A": [ - { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R1", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "SP06", - "subject_id": "TCS-503", - "classroom_id": "R1", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "SS03", - "subject_id": "TCS-502", - "classroom_id": "R1", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R1", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "AK26", - "subject_id": "Placement_Class", - "classroom_id": "R1", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R1", - "time_slot": "3:30 - 4:25" - } - ], - "B": [ - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R2", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "PA21", - "subject_id": "XCS-501", - "classroom_id": "R2", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R2", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "DP07", - "subject_id": "TCS-503", - "classroom_id": "R2", - "time_slot": "12:05 - 1:00" - } - ], - "C": [ - { - "teacher_id": "NB22", - "subject_id": "XCS-501", - "classroom_id": "R3", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AC05", - "subject_id": "TCS-503", - "classroom_id": "R3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R3", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "PK02", - "subject_id": "TCS-531", - "classroom_id": "R3", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R3", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "BJ10", - "subject_id": "TMA-502", - "classroom_id": "R3", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "AA04", - "subject_id": "TCS-502", - "classroom_id": "R3", - "time_slot": "3:30 - 4:25" - } - ], - "D": [ - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L3", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R4", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AC05", - "subject_id": "TCS-502", - "classroom_id": "R4", - "time_slot": "12:05 - 1:00" - } - ] - }, - "Wednesday": { - "A": [ - { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R1", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "11:10 - 12:05" - } - ], - "B": [ - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R2", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "SS03", - "subject_id": "TCS-502", - "classroom_id": "R2", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AB17", - "subject_id": "TCS-509", - "classroom_id": "R2", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "BJ10", - "subject_id": "TMA-502", - "classroom_id": "R2", - "time_slot": "12:05 - 1:00" - } - ], - "C": [ - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R3", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "RS11", - "subject_id": "TMA-502", - "classroom_id": "R3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R3", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "PA21", - "subject_id": "XCS-501", - "classroom_id": "R3", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "DP07", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "AA04", - "subject_id": "TCS-502", - "classroom_id": "R3", - "time_slot": "3:30 - 4:25" - } - ], - "D": [ - { - "teacher_id": "SP06", - "subject_id": "PCS-503", - "classroom_id": "L4", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "SP06", - "subject_id": "PCS-503", - "classroom_id": "L4", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R4", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "NB22", - "subject_id": "XCS-501", - "classroom_id": "R4", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "SP06", - "subject_id": "TCS-503", - "classroom_id": "R4", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "PK02", - "subject_id": "TCS-531", - "classroom_id": "R4", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "HP18", - "subject_id": "TCS-509", - "classroom_id": "R4", - "time_slot": "3:30 - 4:25" - } - ] - }, - "Thursday": { - "A": [ - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L3", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "BJ10", - "subject_id": "TMA-502", - "classroom_id": "R1", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R1", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R1", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "SS03", - "subject_id": "TCS-502", - "classroom_id": "R1", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "SP06", - "subject_id": "TCS-503", - "classroom_id": "R1", - "time_slot": "3:30 - 4:25" - } - ], - "B": [ - { - "teacher_id": "AA04", - "subject_id": "TCS-502", - "classroom_id": "R2", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "DP07", - "subject_id": "TCS-503", - "classroom_id": "R2", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R2", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "RS11", - "subject_id": "TMA-502", - "classroom_id": "R2", - "time_slot": "12:05 - 1:00" - } - ], - "C": [ - { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R3", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "JM12", - "subject_id": "TMA-502", - "classroom_id": "R3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L5", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L5", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AC05", - "subject_id": "TCS-503", - "classroom_id": "R3", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R3", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R3", - "time_slot": "3:30 - 4:25" - } - ], - "D": [ - { - "teacher_id": "NJ13", - "subject_id": "TMA-502", - "classroom_id": "R4", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AP24", - "subject_id": "SCS-501", - "classroom_id": "R4", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "PA21", - "subject_id": "XCS-501", - "classroom_id": "R4", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R4", - "time_slot": "12:05 - 1:00" - } - ] - }, - "Friday": { - "A": [ - { - "teacher_id": "SJ16", - "subject_id": "TCS-509", - "classroom_id": "R1", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R1", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "SP06", - "subject_id": "TCS-503", - "classroom_id": "R1", - "time_slot": "12:05 - 1:00" - } - ], - "B": [ - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "RS11", - "subject_id": "PCS-503", - "classroom_id": "L1", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "BJ10", - "subject_id": "TMA-502", - "classroom_id": "R2", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "PK02", - "subject_id": "TCS-531", - "classroom_id": "R2", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "DP07", - "subject_id": "TCS-503", - "classroom_id": "R2", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R2", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "SS03", - "subject_id": "TCS-502", - "classroom_id": "R2", - "time_slot": "3:30 - 4:25" - } - ], - "C": [ - { - "teacher_id": "AC05", - "subject_id": "TCS-503", - "classroom_id": "R3", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "AK23", - "subject_id": "CSP-501", - "classroom_id": "R3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "AB17", - "subject_id": "TCS-509", - "classroom_id": "R3", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AB01", - "subject_id": "TCS-531", - "classroom_id": "R3", - "time_slot": "12:05 - 1:00" - }, - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L5", - "time_slot": "2:15 - 3:10" - }, - { - "teacher_id": "AD08", - "subject_id": "PCS-506", - "classroom_id": "L5", - "time_slot": "1:20 - 2:15" - }, - { - "teacher_id": "DT20", - "subject_id": "XCS-501", - "classroom_id": "R3", - "time_slot": "3:30 - 4:25" - } - ], - "D": [ - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:55 - 10:50" - }, - { - "teacher_id": "PM14", - "subject_id": "PMA-502", - "classroom_id": "L3", - "time_slot": "9:00 - 9:55" - }, - { - "teacher_id": "HP18", - "subject_id": "TCS-509", - "classroom_id": "R4", - "time_slot": "11:10 - 12:05" - }, - { - "teacher_id": "AA04", - "subject_id": "TCS-502", - "classroom_id": "R4", - "time_slot": "12:05 - 1:00" - } - ] - } - - + schedule1 = { + "Monday": { + "A": [ + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R1", + "time_slot": "3:30 - 4:25", + }, + ], + "B": [ + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L4", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L4", + "time_slot": "11:10 - 12:05", + }, + ], + "C": [ + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00", + }, + ], + "D": [ + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "SG19", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25", + }, + ], + }, + "Tuesday": { + "A": [ + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "11:10 - 12:05", + }, + ], + "B": [ + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "3:30 - 4:25", + }, + ], + "C": [ + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "NB22", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25", + }, + ], + "D": [ + { + "teacher_id": "SG19", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + ], + }, + "Wednesday": { + "A": [ + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L5", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L5", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "3:30 - 4:25", + }, + ], + "B": [ + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + ], + "C": [ + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "L2", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "L2", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25", + }, + ], + "D": [ + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + ], + }, + "Thursday": { + "A": [ + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00", + }, + ], + "B": [ + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "3:30 - 4:25", + }, + ], + "C": [ + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L4", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L4", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "L5", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "L5", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25", + }, + ], + "D": [ + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + ], + }, + "Friday": { + "A": [ + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00", + }, + ], + "B": [ + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "3:30 - 4:25", + }, + ], + "C": [ + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L5", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L5", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25", + }, + ], + "D": [ + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + ], + }, + } + schedule2 = { + "Monday": { + "A": [ + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "3:30 - 4:25", + }, + ], + "B": [ + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "3:30 - 4:25", + }, + ], + "C": [ + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L4", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L4", + "time_slot": "11:10 - 12:05", + }, + ], + "D": [ + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "NB22", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + ], + }, + "Tuesday": { + "A": [ + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AK26", + "subject_id": "Placement_Class", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "3:30 - 4:25", + }, + ], + "B": [ + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + ], + "C": [ + { + "teacher_id": "NB22", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R3", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25", + }, + ], + "D": [ + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + ], + }, + "Wednesday": { + "A": [ + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "11:10 - 12:05", + }, + ], + "B": [ + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + ], + "C": [ + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25", + }, + ], + "D": [ + { + "teacher_id": "SP06", + "subject_id": "PCS-503", + "classroom_id": "L4", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "SP06", + "subject_id": "PCS-503", + "classroom_id": "L4", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "NB22", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25", + }, + ], + }, + "Thursday": { + "A": [ + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "3:30 - 4:25", + }, + ], + "B": [ + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + ], + "C": [ + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25", + }, + ], + "D": [ + { + "teacher_id": "NJ13", + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + ], + }, + "Friday": { + "A": [ + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00", + }, + ], + "B": [ + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "3:30 - 4:25", + }, + ], + "C": [ + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25", + }, + ], + "D": [ + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + ], + }, } diff --git a/TimeTableLib.egg-info/PKG-INFO b/TimeTableLib.egg-info/PKG-INFO new file mode 100644 index 0000000..22d830f --- /dev/null +++ b/TimeTableLib.egg-info/PKG-INFO @@ -0,0 +1,125 @@ +Metadata-Version: 2.1 +Name: TimeTableLib +Version: 0.1.0 +Summary: This Library generates timetable and is a plug and play solution for any python based backend service. +License: MIT +Project-URL: Documentation, https://github.com/TImeTable-GEHU/Time-Table/blob/main/README.md +Project-URL: Source, https://github.com/TImeTable-GEHU/Time-Table +Requires-Python: >=3.7 + +# Timetable Scheduling Project + +## 📚 Overview +Welcome to the **Timetable Scheduling Project**! This repository documents research, findings, discussions, and progress on creating an efficient timetable scheduling system that addresses real-world challenges in academic scheduling. Our goal is to develop a scheduling model that optimally assigns rooms, teachers, and time slots while accommodating various constraints. + +## 🔍 Project Goals +- **Optimal Assignment**: Effectively allocate rooms, teachers, and time slots. +- **Constraint Management**: Consider teacher preferences, student load, room capacities, and subject assignments. +- **Dynamic Adaptability**: Adjust to changes, such as adding new rooms or teachers, without complete model retraining. +- **Simplicity and Flexibility**: Simplify complex constraints while remaining adaptable to real-world needs. + +## 🧬 Genetic Algorithm +The project employs a Genetic Algorithm (GA) to solve the scheduling problem, inspired by the principles of natural selection. Key modules in our GA include: + +1. **Data Encoding and Decoding**: Converting solutions into chromosomes for efficiency. +2. **Initial Population**: Generating a diverse set of potential solutions under specified constraints. +3. **Evaluation of Population**: Using a fitness function to assess solution quality. +4. **Crossover Evolution**: Creating new solutions by combining existing ones. +5. **Mutation**: Introducing random changes to explore new potential solutions. + +### Advantages +- Flexibility compared to manual systems +- Reduced processing power requirements +- Quick generation of accurate timetables +- User-friendly data entry and revision +- Improved productivity and minimal paperwork + +### Disadvantages +- Requires initial raw content input +- High memory consumption +- Internet connectivity needed + +## 📋 Applications +The genetic algorithm can efficiently create various timetables, including: +- University Timetable +- Exam Timetable +- School Timetable +- College Timetable +- Teacher Timetable + +## 📊 Results +The system generates distinct timetables for each class, faculty member, and lab, preventing slot conflicts and allowing customization. The program meets both hard and soft scheduling constraints. + +## ⚙️ Fitness Function +The fitness function is a critical component of our Genetic Algorithm, as it evaluates the quality of each timetable solution. It incorporates both hard and soft constraints to ensure a balanced and effective scheduling outcome. + +### Hard Constraints +These are strict requirements that must be satisfied for a solution to be considered valid: + +1. **No Overlap**: + - Teachers cannot be assigned to multiple classes at the same time. + - Time slots must not overlap for the same section. + - The same classroom cannot be allocated to multiple sections during the same time slot. + +2. **Room Capacity**: + - Each classroom's capacity must accommodate the number of students assigned to that class. + +3. **Time Slot Allocation**: + - Classes must be scheduled within designated time slots. + +4. **Teacher Load Distribution**: + - The total hours assigned to a teacher should not exceed their maximum allowable teaching hours. + +### Soft Constraints +These are preferences that enhance the quality of the solution but are not mandatory: + +1. **Teacher Preferences**: + - Assignments should consider teacher preferences for certain time slots or days. + +2. **Gaps Between Classes**: + - Minimizing the number of gaps between classes for teachers is preferred. + +3. **Balanced Workload**: + - Aim for a balanced distribution of classes among teachers to prevent overloading any single teacher. + +4. **Half Days for Sections** + - Every section should be provided half days so that resources can be utilised properly and also labs and calsses should be balanced. + +### Fitness Score Calculation +The fitness score is calculated based on the following criteria: + +- Each hard constraint violation deducts a significant penalty from the score (e.g., -100 points for each violation). +- Soft constraint preferences add to the score (e.g., +10 points for each preference met). +- The final score is a reflection of both the number of constraints satisfied and the overall quality of the timetable. + +A higher fitness score indicates a better solution, leading to more optimal timetables. The Genetic Algorithm iteratively evolves the population of solutions to maximize this fitness score over generations. + +## 🚀 Future Scope +This project aims to simplify faculty management by automatically assigning courses, considering workload balance, and tracking expertise. Future enhancements may include a master schedule for departments, integration with university websites, and personalized time slot assignments. + +## 🛠️ Installation + +To run this project, clone the repository, set up a virtual environment, and install the required dependencies: + +```bash +git clone https://github.com/yourusername/timetable-scheduling-project.git +cd timetable-scheduling-project + +# Set up a virtual environment +python -m venv venv + +# Activate the virtual environment +# For Linux/macOS: +source venv/bin/activate +# For Windows: +.\venv\Scripts\activate + +# Install dependencies +pip install -r requirements.txt + +# Optionally, set up environment variables in a .env file (e.g., DATABASE_URL, SECRET_KEY) +# Create and edit .env file with necessary variables +touch .env + +echo "Setup complete. You can now run the application." + diff --git a/TimeTableLib.egg-info/SOURCES.txt b/TimeTableLib.egg-info/SOURCES.txt new file mode 100644 index 0000000..707ecfa --- /dev/null +++ b/TimeTableLib.egg-info/SOURCES.txt @@ -0,0 +1,27 @@ +README.md +setup.py +Constants/__init__.py +Constants/classroom_allocate.py +Constants/classroom_tt.py +Constants/classroom_tt_csv.py +Constants/constant.py +Constants/helper_routines.py +Constants/is_conflict.py +Constants/is_conflict_new.py +Constants/section_allocation.py +Constants/teacher_tt_csv.py +Constants/teachers_tt.py +Constants/time_intervals.py +Constants/timetable_csv.py +GA/__init__.py +GA/chromosome.py +GA/fitness.py +GA/mutation.py +GA/selection.py +Samples/__init__.py +Samples/samples.py +TimeTableLib.egg-info/PKG-INFO +TimeTableLib.egg-info/SOURCES.txt +TimeTableLib.egg-info/dependency_links.txt +TimeTableLib.egg-info/requires.txt +TimeTableLib.egg-info/top_level.txt \ No newline at end of file diff --git a/Constants/utils.py b/TimeTableLib.egg-info/dependency_links.txt similarity index 100% rename from Constants/utils.py rename to TimeTableLib.egg-info/dependency_links.txt diff --git a/TimeTableLib.egg-info/requires.txt b/TimeTableLib.egg-info/requires.txt new file mode 100644 index 0000000..6efb963 --- /dev/null +++ b/TimeTableLib.egg-info/requires.txt @@ -0,0 +1,4 @@ +psycopg2-binary==2.9.9 +pytest==7.4.2 +ortools==9.11.4210 +icecream==2.1.3 diff --git a/TimeTableLib.egg-info/top_level.txt b/TimeTableLib.egg-info/top_level.txt new file mode 100644 index 0000000..aaadae1 --- /dev/null +++ b/TimeTableLib.egg-info/top_level.txt @@ -0,0 +1,3 @@ +Constants +GA +Samples diff --git a/__init__.py b/__init__.py index 8fb8fc1..4ce37f4 100644 --- a/__init__.py +++ b/__init__.py @@ -1,107 +1,209 @@ -# This is the Orchestrator file, which will govern the flow. - from Constants.constant import Defaults -from GA.mutation import TimeTableMutation, TimeTableCrossOver -from GA.selection import TimeTableSelection -from GA.fitness import TimetableFitnessEvaluator +from Constants.helper_routines import ( + initialize_teacher_availability, + update_matrix_for_best, + update_teacher_availability_matrix, +) from GA.chromosome import TimeTableGeneration +from GA.fitness import TimetableFitnessEvaluator +from GA.mutation import TimeTableCrossOver, TimeTableMutation +from GA.selection import TimeTableSelection from Samples.samples import ( - SubjectTeacherMap, - TeacherWorkload, + InterDepartment, + RoomCapacity, SpecialSubjects, + SubjectTeacherMap, SubjectWeeklyQuota, - Classrooms, - Sections, - + TeacherWorkload, + TimeSlots, ) -def timetable_generation(): - # Create Chromosomes +def timetable_generation( + teacher_subject_mapping, + total_sections, + total_classrooms, + total_labs, + teacher_preferences, + teacher_weekly_workload, + special_subjects, + labs, + subject_quota_limits, + teacher_duty_days, + teacher_availability_matrix, + time_slots, + prev_selected_chromosomes=None, + prev_mutated_chromosomes=None, +): timetable_generator = TimeTableGeneration( - teacher_subject_mapping=SubjectTeacherMap.subject_teacher_map, - total_sections=6, - total_classrooms=8, - total_labs=3, - teacher_preferences=TeacherWorkload.teacher_preferences, - teacher_weekly_workload=TeacherWorkload.Weekly_workLoad, - special_subjects=SpecialSubjects.special_subjects, - subject_quota_limits=SubjectWeeklyQuota.subject_quota, - labs_list=Classrooms.labs, - teacher_duty_days=TeacherWorkload.teacher_duty_days, + teacher_subject_mapping=teacher_subject_mapping, + total_sections=total_sections, + total_classrooms=total_classrooms, + total_labs=total_labs, + teacher_preferences=teacher_preferences, + teacher_weekly_workload=teacher_weekly_workload, + special_subjects=special_subjects, + labs=labs, + subject_quota_limits=subject_quota_limits, + teacher_duty_days=teacher_duty_days, + teacher_availability_matrix=teacher_availability_matrix, + time_slots=time_slots, + ) + timetable, teacher_availability_matrix = timetable_generator.create_timetable( + Defaults.initial_no_of_chromosomes ) - timetable = timetable_generator.create_timetable(Defaults.initial_no_of_chromosomes) - from icecream import ic - ic(timetable) - # Fitness of each Chromosome fitness_calculator = TimetableFitnessEvaluator( timetable, - timetable_generator.sections_manager.sections, + timetable_generator.sections_manager.keys(), SubjectTeacherMap.subject_teacher_map, - timetable_generator.classrooms_manager.classrooms, - timetable_generator.classrooms_manager.labs, - timetable_generator.room_capacity_manager.room_capacity, - timetable_generator.room_capacity_manager.section_strength, + timetable_generator.classrooms_manager.keys(), + timetable_generator.lab_capacity_manager.keys(), + timetable_generator.classrooms_manager, + timetable_generator.sections_manager, timetable_generator.subject_quota_limits, timetable_generator.teacher_availability_preferences, timetable_generator.weekly_workload, + time_slots, ) - fitness_scores = fitness_calculator.evaluate_timetable_fitness() - - # Selection of all Chromosomes selection_object = TimeTableSelection() selected_chromosomes = selection_object.select_chromosomes(fitness_scores[1]) - ic(len(selected_chromosomes)) - # Crossover for all selected Chromosomes + if prev_selected_chromosomes: + selected_chromosomes.update(prev_selected_chromosomes) + crossover_object = TimeTableCrossOver() crossover_chromosomes = [] - selected_chromosome_keys = list(selected_chromosomes.keys()) - - for i in range(0, len(selected_chromosome_keys), 2): - if i + 1 < len(selected_chromosome_keys): - parent1 = selected_chromosome_keys[i] - parent2 = selected_chromosome_keys[i + 1] - child1, child2 = crossover_object.perform_crossover( - timetable[parent1], - timetable[parent2] + selected_keys = list(selected_chromosomes.keys()) + + for i in range(0, len(selected_keys), 2): + if i + 1 < len(selected_keys): + parent1 = selected_keys[i] + parent2 = selected_keys[i + 1] + c1, c2 = crossover_object.perform_crossover( + timetable[parent1], timetable[parent2] ) + crossover_chromosomes.append(c1) + crossover_chromosomes.append(c2) - crossover_chromosomes.append(child1) - crossover_chromosomes.append(child2) - - - # Mutate all crossover Chromosomes mutation_object = TimeTableMutation() mutated_chromosomes = [ - mutation_object.mutate_schedule_for_week(chromosome) - for chromosome in crossover_chromosomes + mutation_object.mutate_schedule_for_week(ch) for ch in crossover_chromosomes ] + if prev_mutated_chromosomes: + mutated_chromosomes.extend(prev_mutated_chromosomes) - - ic(mutated_chromosomes) - ic(selected_chromosomes) - - ic(selected_chromosomes) - # Store best of Chromosomes best_chromosome_score = -1 - best_chromosome = dict() - - for week_no, week_score in selected_chromosomes.items(): - if int(week_score) > best_chromosome_score: - best_chromosome_score = int(week_score) - best_chromosome = timetable[week_no] - - ic(f"Best Chromosome: {best_chromosome}") - + best_chromosome = None + + for w_no, w_score in selected_chromosomes.items(): + score = int(w_score) + if score > best_chromosome_score and w_no in timetable: + best_chromosome_score = score + best_chromosome = timetable[w_no] + + if best_chromosome: + teacher_availability_matrix = update_teacher_availability_matrix( + teacher_availability_matrix, best_chromosome + ) + + return ( + best_chromosome, + teacher_availability_matrix, + selected_chromosomes, + mutated_chromosomes, + ) -def run_timetable_generation(): - for generation in range(Defaults.total_no_of_generations): - best_chromosome = timetable_generation() - return best_chromosome +def run_timetable_generation( + teacher_subject_mapping, + total_sections, + total_classrooms, + total_labs, + teacher_preferences, + teacher_weekly_workload, + special_subjects, + labs, + subject_quota_limits, + teacher_duty_days, + teacher_availability_matrix, + total_generations, + time_slots, +): + prev_selected = None + prev_mutated = None + best_chromosome = None + + for _ in range(total_generations): + ( + best_chromosome, + teacher_availability_matrix, + selected, + mutated, + ) = timetable_generation( + teacher_subject_mapping=teacher_subject_mapping, + total_sections=total_sections, + total_classrooms=total_classrooms, + total_labs=total_labs, + teacher_preferences=teacher_preferences, + teacher_weekly_workload=teacher_weekly_workload, + special_subjects=special_subjects, + labs=labs, + subject_quota_limits=subject_quota_limits, + teacher_duty_days=teacher_duty_days, + teacher_availability_matrix=teacher_availability_matrix, + time_slots=time_slots, + prev_selected_chromosomes=prev_selected, + prev_mutated_chromosomes=prev_mutated, + ) + prev_selected = selected + prev_mutated = mutated + + return best_chromosome, teacher_availability_matrix + + +if __name__ == "__main__": + best, correct_teacher_availability_matrix = run_timetable_generation( + teacher_subject_mapping=SubjectTeacherMap.subject_teacher_map, + total_sections=RoomCapacity.section_strength, + total_classrooms=RoomCapacity.room_capacity, + total_labs=RoomCapacity.lab_capacity, + teacher_preferences=TeacherWorkload.teacher_preferences, + teacher_weekly_workload=TeacherWorkload.Weekly_workLoad, + special_subjects=SpecialSubjects.special_subjects, + labs=SpecialSubjects.Labs, + subject_quota_limits=SubjectWeeklyQuota.subject_quota, + teacher_duty_days=TeacherWorkload.teacher_duty_days, + teacher_availability_matrix=initialize_teacher_availability( + TeacherWorkload.Weekly_workLoad.keys(), 5, 7 + ), + total_generations=Defaults.total_no_of_generations, + time_slots=TimeSlots.time_slots, + ) + from icecream import ic -run_timetable_generation() \ No newline at end of file + ic(best) + correct_teacher_availability_matrix = update_matrix_for_best( + best, + correct_teacher_availability_matrix, + { + "Monday": 0, + "Tuesday": 1, + "Wednesday": 2, + "Thursday": 3, + "Friday": 4, + "Saturday": 5, + "Sunday": 6, + }, + { + "08:00 - 09:00": 1, + "09:00 - 10:00": 2, + "10:00 - 11:00": 3, + "11:00 - 12:00": 4, + "12:00 - 13:00": 5, + "13:50 - 14:50": 6, + "14:50 - 15:50": 7, + }, + ) diff --git a/all_sections_timetable.html b/all_sections_timetable.html new file mode 100644 index 0000000..993f7db --- /dev/null +++ b/all_sections_timetable.html @@ -0,0 +1,341 @@ +All Sections Timetable

Section A

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Time9:00 - 9:559:55 - 10:5011:10 - 12:0512:05 - 1:001:20 - 2:152:15 - 3:103:30 - 4:25
Day
MondayTCS-531 (AB01)TMA-502 (HP18)PCS-506 (AD08) / PCS-506 (AD08)PCS-506 (AD08) / PCS-506 (AD08)TCS-502 (AA04)TCS-503 (AC05)CSP-501 (AK23)
TuesdayPCS-503 (DP07) / PCS-503 (DP07)PCS-503 (DP07) / PCS-503 (DP07)TCS-502 (SS03)TCS-503 (SP06)TCS-531 (AB01)TMA-502 (HP18)SCS-501 (AP24)
WednesdayTCS-509 (AB17)TCS-503 (DP07)Placement_Class (AK26) / Placement_Class (AK26)Placement_Class (AK26) / Placement_Class (AK26)
ThursdayTCS-502 (SS03)TCS-509 (SJ16)TCS-531 (AB01)TMA-502 (HP18)
FridayPMA-502 (PM14) / PMA-502 (PM14)PMA-502 (PM14) / PMA-502 (PM14)XCS-501 (DT20)TCS-509 (AB17)Library (None)Library (None)Library (None)
SaturdayLibrary (None)Library (None)Library (None)Library (None)

Section B

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Time9:00 - 9:559:55 - 10:5011:10 - 12:0512:05 - 1:001:20 - 2:152:15 - 3:103:30 - 4:25
Day
MondayPMA-502 (PM14) / PMA-502 (PM14)PMA-502 (PM14) / PMA-502 (PM14)TMA-502 (BJ10)TCS-503 (BJ10)SCS-501 (AP24)TCS-531 (PK02)TCS-502 (SS03)
TuesdayTCS-503 (BJ10)TCS-531 (AB01)XCS-501 (DT20)TCS-502 (SS03)
WednesdayTCS-503 (BJ10)TCS-502 (SS03)PCS-503 (RS11) / PCS-503 (RS11)PCS-503 (RS11) / PCS-503 (RS11)TCS-509 (HP18)XCS-501 (DT20)TCS-531 (AB01)
ThursdayCSP-501 (AK23)TCS-509 (SG19)PCS-506 (AD08) / PCS-506 (AD08)PCS-506 (AD08) / PCS-506 (AD08)Placement_Class (AK26) / Placement_Class (AK26)Placement_Class (AK26) / Placement_Class (AK26)TMA-502 (RS11)
FridayTMA-502 (BJ10)TCS-509 (SJ16)Library (None)Library (None)
SaturdayLibrary (None)Library (None)Library (None)Library (None)Library (None)Library (None)Library (None)

Section C

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Time9:00 - 9:559:55 - 10:5011:10 - 12:0512:05 - 1:001:20 - 2:152:15 - 3:103:30 - 4:25
Day
MondayTCS-531 (AB01)TCS-503 (DP07)SCS-501 (AP24)TCS-502 (SS03)
TuesdayTMA-502 (JM12)TCS-509 (AB17)TCS-531 (PK02)CSP-501 (AK23)TCS-503 (SP06)XCS-501 (PA21)TCS-502 (AC05)
WednesdayPCS-506 (AD08) / PCS-506 (AD08)PCS-506 (AD08) / PCS-506 (AD08)TCS-509 (SJ16)TCS-503 (SP06)
ThursdayTCS-502 (AC05)TCS-509 (AB17)Placement_Class (AK26) / Placement_Class (AK26)Placement_Class (AK26) / Placement_Class (AK26)PMA-502 (PM14) / PMA-502 (PM14)PMA-502 (PM14) / PMA-502 (PM14)XCS-501 (DT20)
FridayTCS-531 (PK02)TMA-502 (RS11)PCS-503 (DP07) / PCS-503 (DP07)PCS-503 (DP07) / PCS-503 (DP07)Library (None)Library (None)Library (None)
SaturdayLibrary (None)Library (None)Library (None)Library (None)Library (None)Library (None)Library (None)

Section D

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Time9:00 - 9:559:55 - 10:5011:10 - 12:0512:05 - 1:001:20 - 2:152:15 - 3:103:30 - 4:25
Day
MondayCSP-501 (AK23)TCS-503 (SP06)TCS-509 (SJ16)XCS-501 (DT20)
TuesdayTCS-509 (SJ16)TCS-502 (AA04)PCS-503 (RS11) / PCS-503 (RS11)PCS-503 (RS11) / PCS-503 (RS11)
WednesdayPlacement_Class (AK26) / Placement_Class (AK26)Placement_Class (AK26) / Placement_Class (AK26)TCS-509 (SG19)TMA-502 (JM12)XCS-501 (PA21)SCS-501 (AP24)TCS-531 (PK02)
ThursdayTCS-502 (AA04)TCS-503 (SP06)TCS-531 (PK02)TMA-502 (BJ10)
FridayPCS-506 (AD08) / PCS-506 (AD08)PCS-506 (AD08) / PCS-506 (AD08)TCS-503 (SP06)TCS-531 (AB01)
SaturdayLibrary (None)Library (None)Library (None)Library (None)
\ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..115b3f7 --- /dev/null +++ b/app.py @@ -0,0 +1,61 @@ +from flask import Flask, request, jsonify + +from GA import run_timetable_generation +from flask_cors import CORS + +app = Flask(__name__) +CORS(app) + +@app.route("/generate", methods=["POST"]) +def generate_timetable(): + data = request.get_json() + + # required inputs + teacher_subject_mapping = data.get("teacher_subject_mapping", {}) + total_sections = data.get("total_sections", {}) + total_classrooms = data.get("total_classrooms", {}) + total_labs = data.get("total_labs", {}) + teacher_preferences = data.get("teacher_preferences", {}) + teacher_weekly_workload = data.get("teacher_weekly_workload", {}) + special_subjects = data.get("special_subjects", {}) + labs = data.get("labs", {}) + subject_quota_limits = data.get("subject_quota_limits", {}) + teacher_duty_days = data.get("teacher_duty_days", {}) + teacher_availability_matrix = data.get("teacher_availability_matrix", {}) + lab_availability_matrix = data.get("lab_availability_matrix", {}) + total_generations = data.get("total_generations", 50) + time_slots = {int(k): v for k, v in data.get("time_slots", {}).items()} + day_map = {str(k): int(v) for k, v in data.get("day_map", {}).items()} + time_slot_map = {str(k): int(v) for k, v in data.get("time_slot_map", {}).items()} + fixed_teacher_assignment = data.get("fixed_teacher_assignment", {}) + + # run engine + best_tt, final_teacher, final_lab = run_timetable_generation( + teacher_subject_mapping, + total_sections, + total_classrooms, + total_labs, + teacher_preferences, + teacher_weekly_workload, + special_subjects, + labs, + subject_quota_limits, + teacher_duty_days, + teacher_availability_matrix, + lab_availability_matrix, + total_generations, + time_slots, + day_map, + time_slot_map, + fixed_teacher_assignment, + ) + + return jsonify({ + "timetable": best_tt, + "teacher_availability": final_teacher, + "lab_availability": final_lab + }) + + +if __name__ == "__main__": + app.run(debug=True) \ No newline at end of file diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 0000000..de8c11e --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,18 @@ +# Django Settings +SECRET_KEY=your-secret-key-here +DEBUG=True +ALLOWED_HOSTS=localhost,127.0.0.1 + +# Database +DB_NAME=timetable_db +DB_USER=postgres +DB_PASSWORD=postgres +DB_HOST=localhost +DB_PORT=5432 + +# CORS +CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173 + +# Celery +CELERY_BROKER_URL=redis://localhost:6379/0 +CELERY_RESULT_BACKEND=redis://localhost:6379/0 diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..d255404 --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,47 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +env/ +venv/ +ENV/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# Django +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal +/media +/staticfiles + +# Environment +.env +.venv + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db diff --git a/backend/algorithm/__init__.py b/backend/algorithm/__init__.py new file mode 100644 index 0000000..f6397b7 --- /dev/null +++ b/backend/algorithm/__init__.py @@ -0,0 +1,6 @@ +""" +Algorithm module for timetable generation using Genetic Algorithm. +""" +from algorithm.engine import TimetableEngine, run_timetable_generation + +__all__ = ['TimetableEngine', 'run_timetable_generation'] diff --git a/backend/algorithm/apps.py b/backend/algorithm/apps.py new file mode 100644 index 0000000..b5388c0 --- /dev/null +++ b/backend/algorithm/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AlgorithmConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'algorithm' diff --git a/backend/algorithm/chromosome.py b/backend/algorithm/chromosome.py new file mode 100644 index 0000000..8af691e --- /dev/null +++ b/backend/algorithm/chromosome.py @@ -0,0 +1,354 @@ +import copy +import random +from math import ceil + +from algorithm.constants import Defaults + + +class TimeTableGeneration: + def __init__( + self, + teacher_subject_mapping: dict, + total_sections: dict, + total_classrooms: dict, + total_labs: dict, + teacher_preferences: dict, + teacher_weekly_workload: dict, + special_subjects: dict, + labs: dict, + subject_quota_limits: dict, + teacher_duty_days: dict, + teacher_availability_matrix: dict, + lab_availability_matrix: dict, + time_slots: dict, + fixed_teacher_assignment: dict = None, + ): + self.sections_manager = total_sections + self.classrooms_manager = total_classrooms + self.lab_capacity_manager = total_labs + defaults = Defaults() + self.subject_teacher_mapping = teacher_subject_mapping + self.subject_quota_limits = subject_quota_limits + self.lab_subject_list = labs + self.special_subject_list = special_subjects + self.teacher_availability_preferences = teacher_preferences + self.available_time_slots = time_slots + self.teacher_duty_days = teacher_duty_days + self.weekly_workload = teacher_weekly_workload + self.teacher_availability_matrix = teacher_availability_matrix + self.initial_lab_availability_matrix = copy.deepcopy(lab_availability_matrix) + self.lab_availability_matrix = copy.deepcopy(self.initial_lab_availability_matrix) + self.fixed_teacher_assignment = fixed_teacher_assignment or {} + # Initialize working days + defaults = Defaults() + self.weekdays = defaults.working_days + self._map_sections_to_classrooms() + + def _map_sections_to_classrooms(self) -> dict: + sorted_classrooms = sorted( + self.classrooms_manager.items(), + key = lambda item: item[1], + reverse=True + ) + sorted_sections = sorted( + self.sections_manager.items(), + key = lambda item: item[1], + reverse = True + ) + section_classroom_map = {} + + for section, student_count in sorted_sections: + for index, (classroom, capacity) in enumerate(sorted_classrooms): + if capacity >= student_count: + section_classroom_map[section] = classroom + sorted_classrooms.pop(index) + break + + self.section_to_classroom_map = section_classroom_map + return section_classroom_map + + + def _initialize_teacher_workload_tracker(self) -> dict: + return { + teacher: 0 + for teacher in self.weekly_workload + } + + + def _get_available_subjects(self, section: str, subject_usage_tracker: dict) -> list: + return [ + subject + for subject in self.subject_teacher_mapping + if subject_usage_tracker[section][subject] < self.subject_quota_limits.get(subject, 0) + ] + + + def _assign_subject_and_teacher( + self, + section: str, + slot_index: int, + subjects_scheduled_today: set, + assigned_classroom: str, + subject_usage_tracker: dict, + teacher_workload_tracker: dict, + teacher_availability_matrix: dict, + day_index: int, + ) -> tuple: + available_subjects = self._get_available_subjects(section, subject_usage_tracker) + random.shuffle(available_subjects) + assigned_teacher = None + selected_subject = None + assigned_room = assigned_classroom + + for subject in available_subjects: + if (subject in self.lab_subject_list or subject == "Placement_Class") and slot_index not in [1, 3, 5]: + continue + if subject not in subjects_scheduled_today: + # First, check for a fixed teacher assignment for (section, subject) + fixed_teacher = self.fixed_teacher_assignment.get(section, {}).get(subject) + if fixed_teacher: + if ( + fixed_teacher in teacher_availability_matrix and + len(teacher_availability_matrix[fixed_teacher]) > day_index and + len(teacher_availability_matrix[fixed_teacher][day_index]) > (slot_index - 1) and + teacher_availability_matrix[fixed_teacher][day_index][slot_index - 1] + ): + assigned_teacher = fixed_teacher + teacher_workload_tracker[fixed_teacher] += 1 + selected_subject = subject + subjects_scheduled_today.add(subject) + break + + # Otherwise, assign normally from the available pool + teachers = self.subject_teacher_mapping[subject] + preferred_teachers = [ + t for t in teachers if self.teacher_availability_preferences.get(t, []) + ] + for teacher in sorted(preferred_teachers, key=lambda t: teacher_workload_tracker[t]): + if ( + teacher in teacher_availability_matrix and + len(teacher_availability_matrix[teacher]) > day_index and + len(teacher_availability_matrix[teacher][day_index]) > (slot_index - 1) and + teacher_availability_matrix[teacher][day_index][slot_index - 1] + ): + assigned_teacher = teacher + teacher_workload_tracker[teacher] += 1 + selected_subject = subject + subjects_scheduled_today.add(subject) + break + + if assigned_teacher: + break + + if not assigned_teacher: + selected_subject = "Library" + assigned_teacher = "None" + return assigned_teacher, selected_subject, assigned_room + + + def _allocate_lab( + self, + teacher: str, + subject: str, + day_index: int, + slot_index: int, + section_strength: int, + ) -> tuple: + group1_size = ceil(section_strength / 2) + group2_size = section_strength - group1_size + labs_list = list(self.lab_availability_matrix.keys()) + + for i in range(len(labs_list)): + lab1 = labs_list[i] + if ( + self.lab_availability_matrix[lab1][day_index][slot_index - 1] + and self.lab_availability_matrix[lab1][day_index][slot_index] + and self.lab_capacity_manager.get(lab1, 0) >= group1_size + ): + for j in range(i + 1, len(labs_list)): + lab2 = labs_list[j] + if ( + self.lab_availability_matrix[lab2][day_index][slot_index - 1] + and self.lab_availability_matrix[lab2][day_index][slot_index] + and self.lab_capacity_manager.get(lab2, 0) >= group2_size + ): + self.lab_availability_matrix[lab1][day_index][ + slot_index - 1 + ] = False + self.lab_availability_matrix[lab1][day_index][ + slot_index + ] = False + self.lab_availability_matrix[lab2][day_index][ + slot_index - 1 + ] = False + self.lab_availability_matrix[lab2][day_index][ + slot_index + ] = False + entries = [ + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": lab1, + "time_slot": self.available_time_slots[slot_index], + "group": 1, + }, + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": lab1, + "time_slot": self.available_time_slots[slot_index + 1], + "group": 1, + }, + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": lab2, + "time_slot": self.available_time_slots[slot_index], + "group": 2, + }, + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": lab2, + "time_slot": self.available_time_slots[slot_index + 1], + "group": 2, + }, + ] + return entries, slot_index + 2 + + merged_entry = { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": "merged_lab", + "time_slot": self.available_time_slots[slot_index], + "group": "merged", + "flagged": False, + } + return [merged_entry], slot_index + 1 + + + def _generate_section_schedule( + self, + section: str, + half_day_sections: list, + subject_usage_tracker: dict, + teacher_workload_tracker: dict, + teacher_availability_matrix: dict, + day_index: int, + section_strength: int, + ) -> tuple: + schedule = [] + subjects_scheduled = set() + assigned_classroom = self.section_to_classroom_map[section] + total_slots = 4 if section in half_day_sections else 7 + slot_index = 1 + + while slot_index <= total_slots: + teacher, subject, room = self._assign_subject_and_teacher( + section, + slot_index, + subjects_scheduled, + assigned_classroom, + subject_usage_tracker, + teacher_workload_tracker, + teacher_availability_matrix, + day_index, + ) + time_slot = self.available_time_slots[slot_index] + if subject in self.lab_subject_list or subject == "Placement_Class": + if slot_index <= total_slots - 1: + lab_entries, slot_index = self._allocate_lab( + teacher, subject, day_index, slot_index, section_strength + ) + schedule.extend(lab_entries) + subject_usage_tracker[section][subject] += len(lab_entries) + else: + schedule.append( + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": assigned_classroom, + "time_slot": time_slot, + "group": "fallback", + } + ) + subject_usage_tracker[section][subject] += 1 + slot_index += 1 + else: + schedule.append( + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": room, + "time_slot": time_slot, + "group": "all", + } + ) + if subject != "Library": + subject_usage_tracker[section][subject] += 1 + slot_index += 1 + + return schedule, teacher_availability_matrix + + + def generate_daily_schedule( + self, + sections: list, + half_day_sections: list, + subject_usage_tracker: dict, + day_index: int, + ) -> tuple: + daily_schedule = {} + teacher_workload = self._initialize_teacher_workload_tracker() + for section in sections: + section_strength = self.sections_manager[section] + ( + schedule, + self.teacher_availability_matrix, + ) = self._generate_section_schedule( + section, + half_day_sections, + subject_usage_tracker, + teacher_workload, + self.teacher_availability_matrix, + day_index, + section_strength, + ) + daily_schedule[section] = schedule + return daily_schedule, subject_usage_tracker, self.teacher_availability_matrix + + + def _generate_weekly_schedule(self) -> tuple: + weekly_schedule = {} + subject_usage = { + section: {subject: 0 for subject in self.subject_teacher_mapping.keys()} + for section in self.sections_manager.keys() + } + sections = list(self.sections_manager.keys()) + for day_index, weekday in enumerate(self.weekdays): + random.shuffle(sections) + half_day = sections[: len(sections) // 2] + ( + daily_sched, + subject_usage, + self.teacher_availability_matrix, + ) = self.generate_daily_schedule( + sections, half_day, subject_usage, day_index + ) + weekly_schedule[weekday] = daily_sched + return weekly_schedule, subject_usage, self.teacher_availability_matrix + + + def create_timetable(self, num_weeks: int) -> tuple: + timetable = {} + for week in range(1, num_weeks + 1): + self.lab_availability_matrix = copy.deepcopy( + self.initial_lab_availability_matrix + ) + ( + weekly_schedule, + _, + self.teacher_availability_matrix, + ) = self._generate_weekly_schedule() + timetable[f"Week {week}"] = weekly_schedule + return timetable, self.teacher_availability_matrix, self.lab_availability_matrix diff --git a/backend/algorithm/constants.py b/backend/algorithm/constants.py new file mode 100644 index 0000000..a7afea5 --- /dev/null +++ b/backend/algorithm/constants.py @@ -0,0 +1,30 @@ +""" +Constants for the timetable generation algorithm. +""" + + +class Defaults: + """Default configuration values.""" + def __init__(self, config=None): + config = config or {} + self.room_capacity = config.get('room_capacity', 60) + self.working_days = config.get('working_days', [ + "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" + ]) + self.starting_section_fitness = config.get('starting_section_fitness', 1000) + self.max_class_capacity = config.get('max_class_capacity', 250) + self.initial_no_of_chromosomes = config.get('initial_no_of_chromosomes', 10) + self.total_no_of_generations = config.get('total_no_of_generations', 50) + self.class_strength = config.get('class_strength', 50) + + +class PenaltyConstants: + """Penalty values for constraint violations.""" + def __init__(self, config=None): + config = config or {} + self.PENALTY_TEACHER_DOUBLE_BOOKED = config.get('teacher_double_booked', 30) + self.PENALTY_CLASSROOM_DOUBLE_BOOKED = config.get('classroom_double_booked', 20) + self.PENALTY_OVER_CAPACITY = config.get('over_capacity', 25) + self.PENALTY_UN_PREFERRED_SLOT = config.get('un_preferred_slot', 5) + self.PENALTY_OVERLOAD_TEACHER = config.get('overload_teacher', 10) + self.PENALTY_NON_DUTY_DAY = config.get('non_duty_day', 40) diff --git a/backend/algorithm/engine.py b/backend/algorithm/engine.py new file mode 100644 index 0000000..a15bdf8 --- /dev/null +++ b/backend/algorithm/engine.py @@ -0,0 +1,229 @@ +""" +Timetable generation engine using Genetic Algorithm. +""" +import copy +from dataclasses import dataclass +from typing import Dict, List, Tuple, Optional + +from algorithm.chromosome import TimeTableGeneration +from algorithm.fitness import TimetableFitnessEvaluator +from algorithm.mutation import TimeTableCrossOver, TimeTableMutation +from algorithm.selection import TimeTableSelection +from algorithm.helpers import ( + initialize_teacher_availability, + update_matrix_for_best, + update_teacher_availability_matrix, +) +from algorithm.constants import Defaults + + +@dataclass +class TimetableConfig: + """Configuration for timetable generation.""" + teacher_subject_mapping: dict + total_sections: dict + total_classrooms: dict + total_labs: dict + teacher_preferences: dict + teacher_weekly_workload: dict + special_subjects: dict + labs: dict + subject_quota_limits: dict + teacher_duty_days: dict + teacher_availability_matrix: dict + lab_availability_matrix: dict + total_generations: int + time_slots: dict + day_map: dict + time_slot_map: dict + prev_selected: dict = None + prev_mutated: list = None + fixed_teacher_assignment: dict = None + working_days: list = None + + +class TimetableEngine: + """Main engine for generating timetables using Genetic Algorithm.""" + + def __init__(self, config: TimetableConfig): + self.config = config + self.teacher_availability = copy.deepcopy(config.teacher_availability_matrix) + self.lab_availability = copy.deepcopy(config.lab_availability_matrix) + + # Set working days + if config.working_days: + from algorithm.constants import Defaults + defaults = Defaults({'working_days': config.working_days}) + self.working_days = defaults.working_days + else: + defaults = Defaults() + self.working_days = defaults.working_days + + def _update_lab_availability(self, best_timetable): + """Update lab availability based on best timetable.""" + updated_lab = copy.deepcopy(self.config.lab_availability_matrix) + for weekday, daily_schedule in best_timetable.items(): + day_index = self.config.day_map.get(weekday) + if day_index is None: + continue + for allocations in daily_schedule.values(): + for alloc in allocations: + lab = alloc.get("classroom_id") + ts_index = self.config.time_slot_map.get(alloc.get("time_slot")) + if lab in updated_lab and ts_index is not None: + if (day_index < len(updated_lab[lab]) and + ts_index - 1 < len(updated_lab[lab][day_index])): + updated_lab[lab][day_index][ts_index - 1] = False + return updated_lab + + def _generate_timetable(self, teacher_matrix): + """Generate a single generation of timetables.""" + tg = TimeTableGeneration( + teacher_subject_mapping=self.config.teacher_subject_mapping, + total_sections=self.config.total_sections, + total_classrooms=self.config.total_classrooms, + total_labs=self.config.total_labs, + teacher_preferences=self.config.teacher_preferences, + teacher_weekly_workload=self.config.teacher_weekly_workload, + special_subjects=self.config.special_subjects, + labs=self.config.labs, + subject_quota_limits=self.config.subject_quota_limits, + teacher_duty_days=self.config.teacher_duty_days, + teacher_availability_matrix=teacher_matrix, + lab_availability_matrix=self.lab_availability, + time_slots=self.config.time_slots, + fixed_teacher_assignment=self.config.fixed_teacher_assignment or {} + ) + + # Generate multiple weeks (chromosomes) + num_weeks = self.config.total_generations if self.config.total_generations < 10 else 10 + timetable, updated_teacher, _ = tg.create_timetable(num_weeks) + + # Evaluate fitness + fitness = TimetableFitnessEvaluator( + timetable=timetable, + all_sections=list(tg.sections_manager.keys()), + subject_teacher_mapping=tg.subject_teacher_mapping, + available_classrooms=list(tg.classrooms_manager.keys()), + available_labs=list(tg.lab_capacity_manager.keys()), + classroom_capacity=tg.classrooms_manager, + section_student_strength=tg.sections_manager, + subject_quota_data=tg.subject_quota_limits, + teacher_time_preferences=tg.teacher_availability_preferences, + teacher_daily_workload=tg.weekly_workload, + time_slots=self.config.time_slots, + ).evaluate_timetable_fitness() + + # Selection + selected = TimeTableSelection().select_chromosomes(fitness[1]) + if self.config.prev_selected: + selected.update(self.config.prev_selected) + + # Crossover + crossover = TimeTableCrossOver() + crossover_chromosomes = [] + selected_keys = list(selected.keys()) + for i in range(0, len(selected_keys), 2): + if i + 1 < len(selected_keys): + c1, c2 = crossover.perform_crossover( + timetable[selected_keys[i]], timetable[selected_keys[i + 1]] + ) + crossover_chromosomes.extend([c1, c2]) + + # Mutation + mutated = [TimeTableMutation().mutate_schedule_for_week(ch) for ch in crossover_chromosomes] + if self.config.prev_mutated: + mutated.extend(self.config.prev_mutated) + + # Find best chromosome + best_chromosome, best_score = None, -1 + for key, score in selected.items(): + score = int(score) + if score > best_score and key in timetable: + best_score = score + best_chromosome = timetable[key] + + if best_chromosome: + updated_teacher = update_teacher_availability_matrix(teacher_matrix, best_chromosome) + + return best_chromosome, updated_teacher, selected, mutated, best_score + + def run(self): + """Run the genetic algorithm to generate optimal timetable.""" + initial_teacher = copy.deepcopy(self.teacher_availability) + updated_teacher = copy.deepcopy(initial_teacher) + best_chromosome = None + best_score = -1 + + self.config.prev_selected = None + self.config.prev_mutated = None + + # Run for specified number of generations + for generation in range(self.config.total_generations): + teacher_copy = copy.deepcopy(initial_teacher) + best, updated_teacher, selected, mutated, score = self._generate_timetable(teacher_copy) + + if best and score > best_score: + best_chromosome = best + best_score = score + + self.config.prev_selected = selected + self.config.prev_mutated = mutated + + # Update teacher availability for final best timetable + if best_chromosome: + updated_teacher = update_matrix_for_best( + best_chromosome, + updated_teacher, + self.config.day_map, + self.config.time_slot_map, + ) + + updated_lab = self._update_lab_availability(best_chromosome) if best_chromosome else self.lab_availability + + return best_chromosome, updated_teacher, updated_lab, best_score + + +def run_timetable_generation( + teacher_subject_mapping: dict, + total_sections: dict, + total_classrooms: dict, + total_labs: dict, + teacher_preferences: dict, + teacher_weekly_workload: dict, + special_subjects: dict, + labs: dict, + subject_quota_limits: dict, + teacher_duty_days: dict, + teacher_availability_matrix: dict, + lab_availability_matrix: dict, + total_generations: int, + time_slots: dict, + day_map: dict, + time_slot_map: dict, + fixed_teacher_assignment: dict = None, + working_days: list = None, +): + """Main function to run timetable generation.""" + config = TimetableConfig( + teacher_subject_mapping=teacher_subject_mapping, + total_sections=total_sections, + total_classrooms=total_classrooms, + total_labs=total_labs, + teacher_preferences=teacher_preferences, + teacher_weekly_workload=teacher_weekly_workload, + special_subjects=special_subjects, + labs=labs, + subject_quota_limits=subject_quota_limits, + teacher_duty_days=teacher_duty_days, + teacher_availability_matrix=teacher_availability_matrix, + lab_availability_matrix=lab_availability_matrix, + total_generations=total_generations, + time_slots=time_slots, + day_map=day_map, + time_slot_map=time_slot_map, + fixed_teacher_assignment=fixed_teacher_assignment or {}, + working_days=working_days, + ) + engine = TimetableEngine(config) + return engine.run() diff --git a/backend/algorithm/fitness.py b/backend/algorithm/fitness.py new file mode 100644 index 0000000..3fe5248 --- /dev/null +++ b/backend/algorithm/fitness.py @@ -0,0 +1,186 @@ +import json + +from algorithm.constants import Defaults, PenaltyConstants +from algorithm.chromosome import TimeTableGeneration + + +class TimetableFitnessEvaluator: + def __init__( + self, + timetable, + all_sections, + subject_teacher_mapping, + available_classrooms, + available_labs, + classroom_capacity, + section_student_strength, + subject_quota_data, + teacher_time_preferences, + teacher_daily_workload, + time_slots, + config=None, + ): + self.timetable = timetable + self.defaults = Defaults(config) + self.penalty_constants = PenaltyConstants(config) + self.available_days = self.defaults.working_days + self.all_sections = all_sections + self.subject_teacher_mapping = subject_teacher_mapping + self.available_classrooms = available_classrooms + self.available_labs = available_labs + self.classroom_capacity = classroom_capacity + self.section_student_strength = section_student_strength + self.subject_quota_data = subject_quota_data + self.teacher_time_preferences = teacher_time_preferences + self.teacher_daily_workload = teacher_daily_workload + self.time_slots = time_slots + + def evaluate_timetable_fitness(self): + daily_section_fitness_scores = {} + weekly_fitness_scores = {} + teacher_workload_tracking = {} + + for week, week_schedule in self.timetable.items(): + weekly_fitness = 0 + daily_section_fitness_scores[week] = {} + teacher_time_slot_tracking = {} + for day, day_schedule in week_schedule.items(): + daily_section_fitness_scores[week][day] = {} + day_fitness = 0 + for section, section_schedule in day_schedule.items(): + section_fitness = self.defaults.starting_section_fitness + + classroom_time_slot_tracking = {} + + for schedule_item in section_schedule: + assigned_teacher = schedule_item["teacher_id"] + assigned_classroom = schedule_item["classroom_id"] + assigned_time_slot = self.time_slots.get( + schedule_item["time_slot"] + ) + section_strength = self.section_student_strength.get(section, 0) + + # Penalty 1: Double booking a teacher in the same time slot + if ( + assigned_teacher, + assigned_time_slot, + ) in teacher_time_slot_tracking.keys(): + section_fitness -= ( + self.penalty_constants.PENALTY_TEACHER_DOUBLE_BOOKED + ) + else: + teacher_time_slot_tracking[ + (assigned_teacher, assigned_time_slot) + ] = section + + # Penalty 2: Double booking a classroom in the same time slot + if ( + assigned_classroom, + assigned_time_slot, + ) in classroom_time_slot_tracking: + section_fitness -= ( + self.penalty_constants.PENALTY_CLASSROOM_DOUBLE_BOOKED + ) + else: + classroom_time_slot_tracking[ + (assigned_classroom, assigned_time_slot) + ] = section + + # Penalty 3: Over-capacity classrooms + if section_strength > self.classroom_capacity.get( + assigned_classroom, self.defaults.max_class_capacity + ): + section_fitness -= self.penalty_constants.PENALTY_OVER_CAPACITY + + # Penalty 4: Assigning teachers during unpreferred time slots + preferred_time_slots = self.teacher_time_preferences.get( + assigned_teacher, [] + ) + if assigned_time_slot not in preferred_time_slots: + section_fitness -= ( + self.penalty_constants.PENALTY_UN_PREFERRED_SLOT + ) + + # Penalty 5: Scheduling teacher on a non-duty day + # Note: teacher_duty_days should be passed as a parameter if needed + # For now, we'll skip this check or make it optional + pass + + # Tracking teacher workload + if assigned_teacher not in teacher_workload_tracking: + teacher_workload_tracking[assigned_teacher] = [] + teacher_workload_tracking[assigned_teacher].append( + assigned_time_slot + ) + + # Penalty 6: Exceeding teacher daily workload + for teacher, times_assigned in teacher_workload_tracking.items(): + if teacher is not None: + if len(times_assigned) > self.teacher_daily_workload.get( + teacher, 0 + ): + section_fitness -= ( + self.penalty_constants.PENALTY_OVERLOAD_TEACHER + ) + + daily_section_fitness_scores[week][day][section] = section_fitness + day_fitness += section_fitness + + weekly_fitness += day_fitness + + weekly_fitness_scores[week] = weekly_fitness + + return daily_section_fitness_scores, weekly_fitness_scores + + +if __name__ == "__main__": + total_sections = 6 + total_classrooms = 8 + total_labs = 3 + + # Generate timetable + timetable_generator = TimeTableGeneration( + teacher_subject_mapping=SubjectTeacherMap.subject_teacher_map, + total_sections=total_sections, + total_classrooms=total_classrooms, + total_labs=total_labs, + teacher_preferences=TeacherWorkload.teacher_preferences, + teacher_weekly_workload=TeacherWorkload.Weekly_workLoad, + special_subjects=SpecialSubjects.special_subjects, + subject_quota_limits=SubjectWeeklyQuota.subject_quota, + teacher_duty_days=TeacherWorkload.teacher_duty_days, + ) + generated_timetables = timetable_generator.create_timetable(5) + + # Evaluate fitness + fitness_evaluator = TimetableFitnessEvaluator( + generated_timetables, + timetable_generator.sections_manager.sections, + SubjectTeacherMap.subject_teacher_map, + timetable_generator.classrooms_manager.classrooms, + timetable_generator.classrooms_manager.labs, + timetable_generator.room_capacity_manager.room_capacity, + timetable_generator.room_capacity_manager.section_strength, + timetable_generator.subject_quota_limits, + timetable_generator.teacher_availability_preferences, + timetable_generator.weekly_workload, + ) + + ( + section_fitness_data, + weekly_fitness_data, + ) = fitness_evaluator.evaluate_timetable_fitness() + + # Save results + with open("GA/chromosome.json", "w") as timetable_file: + json.dump(generated_timetables, timetable_file, indent=4) + + fitness_output_data = { + "section_fitness_scores": section_fitness_data, + "weekly_fitness_scores": weekly_fitness_data, + } + + with open("GA/fitness.json", "w") as fitness_scores_file: + json.dump(fitness_output_data, fitness_scores_file, indent=4) + + print("Timetable and fitness scores have been saved.") diff --git a/backend/algorithm/helpers.py b/backend/algorithm/helpers.py new file mode 100644 index 0000000..e579b3a --- /dev/null +++ b/backend/algorithm/helpers.py @@ -0,0 +1,46 @@ +""" +Helper functions for timetable generation. +""" + + +def initialize_teacher_availability(teacher_list, num_days=5, num_slots=7): + """Initialize teacher availability matrix.""" + return { + teacher: [[True] * num_slots for _ in range(num_days)] + for teacher in teacher_list + } + + +def update_teacher_availability_matrix(teacher_availability_matrix, best_chromosome): + """Update teacher availability matrix based on best chromosome.""" + for teacher_id, schedule_data in best_chromosome.items(): + if teacher_id in teacher_availability_matrix: + teacher_availability_matrix[teacher_id] = schedule_data + return teacher_availability_matrix + + +def update_matrix_for_best(best_chromosome, teacher_availability_matrix, day_map, time_slot_map): + """Update teacher availability matrix for the best timetable.""" + for day_name, section_dict in best_chromosome.items(): + if day_name not in day_map: + continue + day_index = day_map[day_name] + + for section_name, schedule_items in section_dict.items(): + for item in schedule_items: + teacher_id = item.get("teacher_id") + if teacher_id == "None" or not teacher_id: + continue + + time_slot_str = item.get("time_slot") + if time_slot_str not in time_slot_map: + continue + slot_index = time_slot_map[time_slot_str] + + # Set availability to False + if (teacher_id in teacher_availability_matrix and + day_index < len(teacher_availability_matrix[teacher_id]) and + slot_index - 1 < len(teacher_availability_matrix[teacher_id][day_index])): + teacher_availability_matrix[teacher_id][day_index][slot_index - 1] = False + + return teacher_availability_matrix diff --git a/backend/algorithm/mutation.py b/backend/algorithm/mutation.py new file mode 100644 index 0000000..fd582d6 --- /dev/null +++ b/backend/algorithm/mutation.py @@ -0,0 +1,39 @@ +import copy +import random + +from algorithm.constants import Defaults + + +class TimeTableMutation: + def __init__(self, mutation_rate=0.7): + self.mutation_rate = mutation_rate + + def mutate_time_slots_in_section(self, schedule: dict, section: str) -> bool: + if section not in schedule or len(schedule[section]) < 2: + return False + + section_slots = schedule[section] + time_slots = [entry["time_slot"] for entry in section_slots] + random.shuffle(time_slots) + for i, entry in enumerate(section_slots): + entry["time_slot"] = time_slots[i] + return True + + def mutate_schedule_for_week(self, weekly_schedule: dict) -> dict: + mutated_schedule = copy.deepcopy(weekly_schedule) + for day, day_schedule in mutated_schedule.items(): + sections = list(day_schedule.keys()) + num_to_mutate = max(1, int(self.mutation_rate * len(sections))) + sections_to_mutate = random.sample(sections, num_to_mutate) + for section in sections_to_mutate: + self.mutate_time_slots_in_section(day_schedule, section) + return mutated_schedule + + +class TimeTableCrossOver: + def perform_crossover(self, timetable1: dict, timetable2: dict) -> tuple: + defaults = Defaults() + for day in defaults.working_days: + if day in timetable1 and day in timetable2: + timetable1[day], timetable2[day] = timetable2[day], timetable1[day] + return timetable1, timetable2 diff --git a/backend/algorithm/selection.py b/backend/algorithm/selection.py new file mode 100644 index 0000000..b761136 --- /dev/null +++ b/backend/algorithm/selection.py @@ -0,0 +1,97 @@ +import random + + +class TimeTableSelection: + def __init__(self): + pass + + def select_chromosomes( + self, weekly_fitness_scores, top_percentage=0.20, roulette_percentage=0.10 + ): + """ + Entry point for selecting chromosomes. + Select chromosomes based on top scores and roulette-wheel selection. + """ + + if not weekly_fitness_scores: + print("Weekly fitness scores are empty. Ensure data is loaded correctly!") + return {} + + top_selected, remaining_scores = self.get_top_and_remaining_items( + weekly_fitness_scores, top_percentage + ) + + roulette_selected = self.roulette_wheel_selection( + remaining_scores, int(len(weekly_fitness_scores) * roulette_percentage) + ) + + selected_fitness = {**top_selected, **roulette_selected} + # self.display_selected_chromosomes(selected_fitness) + return selected_fitness + + @staticmethod + def calculate_cumulative_probabilities(scores): + """ + Calculate cumulative probabilities for roulette wheel selection. + """ + + cumulative_probabilities = [] + cumulative_sum = 0 + + for week, score in scores.items(): + cumulative_sum += score + cumulative_probabilities.append((cumulative_sum, week)) + + return cumulative_probabilities + + @staticmethod + def perform_roulette_selection(cumulative_probabilities, total_fitness, num_select): + """ + Perform roulette-wheel selection to choose items. + """ + + selected_items = [] + for _ in range(num_select): + rand_value = random.uniform(0, total_fitness) + for cumulative_sum, week in cumulative_probabilities: + if rand_value <= cumulative_sum: + selected_items.append(week) + break + + return selected_items + + def roulette_wheel_selection(self, scores, num_select): + """ + Select items using roulette-wheel selection. + """ + + if not scores: + print("Scores are empty. Cannot perform roulette selection.") + return {} + + total_fitness = sum(scores.values()) + cumulative_probabilities = self.calculate_cumulative_probabilities(scores) + selected_items = self.perform_roulette_selection( + cumulative_probabilities, total_fitness, num_select + ) + return {week: scores[week] for week in selected_items} + + @staticmethod + def get_top_and_remaining_items(scores, percentage): + num_select = max( + 1, int(len(scores) * percentage) + ) # at least 1 chromosome to select. + sorted_scores = sorted(scores.items(), key=lambda x: x[1], reverse=True) + top_selected = dict(sorted_scores[:num_select]) + remaining_items = dict(sorted_scores[num_select:]) + return top_selected, remaining_items + + @staticmethod + def display_selected_chromosomes(selected_fitness): + """ + Display the selected chromosomes and their fitness scores. + """ + + print("\n--- Selected Weeks and Fitness Scores ---") + for week, score in selected_fitness.items(): + print(f"Week: {week}, Score: {score}") diff --git a/backend/api/__init__.py b/backend/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/api/apps.py b/backend/api/apps.py new file mode 100644 index 0000000..66656fd --- /dev/null +++ b/backend/api/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ApiConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'api' diff --git a/backend/api/serializers.py b/backend/api/serializers.py new file mode 100644 index 0000000..a6287c6 --- /dev/null +++ b/backend/api/serializers.py @@ -0,0 +1,49 @@ +""" +Serializers for authentication. +""" +from rest_framework import serializers +from core.models import User +from django.contrib.auth.password_validation import validate_password + + +class UserRegistrationSerializer(serializers.ModelSerializer): + """Serializer for user registration.""" + password = serializers.CharField(write_only=True, required=True, validators=[validate_password]) + password2 = serializers.CharField(write_only=True, required=True, label='Confirm Password') + + class Meta: + model = User + fields = ['email', 'first_name', 'last_name', 'password', 'password2', 'role'] + extra_kwargs = { + 'first_name': {'required': True}, + 'last_name': {'required': True}, + } + + def validate(self, attrs): + if attrs['password'] != attrs['password2']: + raise serializers.ValidationError({"password": "Password fields didn't match."}) + return attrs + + def create(self, validated_data): + validated_data.pop('password2') + password = validated_data.pop('password') + user = User.objects.create_user(password=password, **validated_data) + return user + + +class UserLoginSerializer(serializers.Serializer): + """Serializer for user login.""" + email = serializers.EmailField(required=True) + password = serializers.CharField(required=True, write_only=True) + + +class UserSerializer(serializers.ModelSerializer): + """Serializer for user details.""" + full_name = serializers.ReadOnlyField() + college_name = serializers.CharField(source='college.name', read_only=True) + + class Meta: + model = User + fields = ['id', 'email', 'first_name', 'last_name', 'full_name', + 'role', 'college', 'college_name', 'is_active', 'date_joined'] + read_only_fields = ['id', 'date_joined'] diff --git a/backend/api/urls.py b/backend/api/urls.py new file mode 100644 index 0000000..69a20b6 --- /dev/null +++ b/backend/api/urls.py @@ -0,0 +1,12 @@ +""" +URLs for authentication API. +""" +from django.urls import path +from api import views + +urlpatterns = [ + path('register/', views.register, name='register'), + path('login/', views.login, name='login'), + path('me/', views.me, name='me'), + path('logout/', views.logout, name='logout'), +] diff --git a/backend/api/views.py b/backend/api/views.py new file mode 100644 index 0000000..0ed42e5 --- /dev/null +++ b/backend/api/views.py @@ -0,0 +1,73 @@ +""" +Authentication views. +""" +from rest_framework import status, generics +from rest_framework.decorators import api_view, permission_classes +from rest_framework.response import Response +from rest_framework.permissions import AllowAny, IsAuthenticated +from rest_framework_simplejwt.tokens import RefreshToken +from django.contrib.auth import authenticate +from api.serializers import UserRegistrationSerializer, UserLoginSerializer, UserSerializer +from core.models import User + + +@api_view(['POST']) +@permission_classes([AllowAny]) +def register(request): + """Register a new user.""" + serializer = UserRegistrationSerializer(data=request.data) + if serializer.is_valid(): + user = serializer.save() + refresh = RefreshToken.for_user(user) + return Response({ + 'user': UserSerializer(user).data, + 'refresh': str(refresh), + 'access': str(refresh.access_token), + }, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +@api_view(['POST']) +@permission_classes([AllowAny]) +def login(request): + """Login user and return JWT tokens.""" + serializer = UserLoginSerializer(data=request.data) + if serializer.is_valid(): + email = serializer.validated_data['email'] + password = serializer.validated_data['password'] + user = authenticate(request, username=email, password=password) + + if user is not None and user.is_active: + refresh = RefreshToken.for_user(user) + return Response({ + 'user': UserSerializer(user).data, + 'refresh': str(refresh), + 'access': str(refresh.access_token), + }, status=status.HTTP_200_OK) + else: + return Response( + {'error': 'Invalid credentials or inactive account'}, + status=status.HTTP_401_UNAUTHORIZED + ) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +def me(request): + """Get current user details.""" + serializer = UserSerializer(request.user) + return Response(serializer.data) + + +@api_view(['POST']) +@permission_classes([IsAuthenticated]) +def logout(request): + """Logout user by blacklisting refresh token.""" + try: + refresh_token = request.data.get('refresh') + token = RefreshToken(refresh_token) + token.blacklist() + return Response({'message': 'Successfully logged out'}, status=status.HTTP_200_OK) + except Exception as e: + return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST) diff --git a/backend/core/__init__.py b/backend/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/core/admin.py b/backend/core/admin.py new file mode 100644 index 0000000..38ae9d5 --- /dev/null +++ b/backend/core/admin.py @@ -0,0 +1,103 @@ +""" +Django admin configuration. +""" +from django.contrib import admin +from django.contrib.auth.admin import UserAdmin as BaseUserAdmin +from core.models import ( + User, College, Department, Teacher, Subject, Section, + Classroom, SubjectTeacherMapping, Timetable, TeacherPreference +) + + +@admin.register(College) +class CollegeAdmin(admin.ModelAdmin): + list_display = ['name', 'code', 'subdomain', 'is_active', 'created_at'] + list_filter = ['is_active', 'created_at'] + search_fields = ['name', 'code', 'subdomain'] + readonly_fields = ['id', 'created_at', 'updated_at'] + + +from django.contrib import admin +from django.contrib.auth.admin import UserAdmin as BaseUserAdmin +from core.models import User + + +@admin.register(User) +class UserAdmin(BaseUserAdmin): + ordering = ('email',) + + list_display = ('email', 'full_name', 'role', 'is_active', 'is_staff') + list_filter = ('role', 'is_active', 'is_staff') + search_fields = ('email', 'first_name', 'last_name') + + fieldsets = ( + (None, {'fields': ('email', 'password')}), + ('Personal info', {'fields': ('first_name', 'last_name', 'role', 'college')}), + ('Permissions', {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}), + ('Important dates', {'fields': ('last_login', 'date_joined')}), + ) + + add_fieldsets = ( + (None, { + 'classes': ('wide',), + 'fields': ('email', 'first_name', 'last_name', 'password1', 'password2', 'role', 'college'), + }), + ) + + filter_horizontal = ('groups', 'user_permissions') + + +@admin.register(Department) +class DepartmentAdmin(admin.ModelAdmin): + list_display = ['name', 'code', 'college', 'hod', 'created_at'] + list_filter = ['college', 'created_at'] + search_fields = ['name', 'code'] + + +@admin.register(Teacher) +class TeacherAdmin(admin.ModelAdmin): + list_display = ['user', 'employee_id', 'department', 'designation', 'max_weekly_hours'] + list_filter = ['department', 'designation'] + search_fields = ['user__email', 'user__first_name', 'user__last_name', 'employee_id'] + + +@admin.register(Subject) +class SubjectAdmin(admin.ModelAdmin): + list_display = ['code', 'name', 'college', 'credits', 'weekly_quota', 'is_lab'] + list_filter = ['college', 'is_lab', 'department'] + search_fields = ['code', 'name'] + + +@admin.register(Section) +class SectionAdmin(admin.ModelAdmin): + list_display = ['name', 'college', 'student_strength', 'year', 'semester'] + list_filter = ['college', 'year', 'semester'] + search_fields = ['name'] + + +@admin.register(Classroom) +class ClassroomAdmin(admin.ModelAdmin): + list_display = ['name', 'college', 'capacity', 'is_lab'] + list_filter = ['college', 'is_lab'] + search_fields = ['name'] + + +@admin.register(SubjectTeacherMapping) +class SubjectTeacherMappingAdmin(admin.ModelAdmin): + list_display = ['subject', 'teacher', 'section', 'is_primary'] + list_filter = ['is_primary', 'subject__college'] + search_fields = ['subject__code', 'teacher__user__email'] + + +@admin.register(Timetable) +class TimetableAdmin(admin.ModelAdmin): + list_display = ['name', 'college', 'academic_year', 'status', 'fitness_score', 'created_at'] + list_filter = ['status', 'college', 'created_at'] + search_fields = ['name', 'academic_year'] + readonly_fields = ['id', 'created_at', 'updated_at'] + + +@admin.register(TeacherPreference) +class TeacherPreferenceAdmin(admin.ModelAdmin): + list_display = ['teacher', 'created_at', 'updated_at'] + search_fields = ['teacher__user__email'] diff --git a/backend/core/apps.py b/backend/core/apps.py new file mode 100644 index 0000000..8115ae6 --- /dev/null +++ b/backend/core/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CoreConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'core' diff --git a/backend/core/management/__init__.py b/backend/core/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/core/management/commands/__init__.py b/backend/core/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/core/management/commands/load_sample_data.py b/backend/core/management/commands/load_sample_data.py new file mode 100644 index 0000000..cd70582 --- /dev/null +++ b/backend/core/management/commands/load_sample_data.py @@ -0,0 +1,229 @@ +""" +Management command to load sample data for demonstration. +""" +from django.core.management.base import BaseCommand +from core.models import ( + College, User, Department, Teacher, Subject, Section, + Classroom, SubjectTeacherMapping, TeacherPreference +) +from algorithm.helpers import initialize_teacher_availability + + +class Command(BaseCommand): + help = 'Load sample data for demonstration' + + def handle(self, *args, **options): + self.stdout.write('Loading sample data...') + + # Create or get master admin + master_admin, created = User.objects.get_or_create( + email='ayushkapri18@gmail.com', + defaults={ + 'first_name': 'Ayush', + 'last_name': 'Kapri', + 'role': User.Role.MASTER_ADMIN, + 'is_staff': True, + 'is_superuser': True, + } + ) + if created: + master_admin.set_password('admin123') + master_admin.save() + self.stdout.write(self.style.SUCCESS('Created master admin (admin@timetable.com / admin123)')) + + # Create sample college + college, created = College.objects.get_or_create( + code='GEHU', + defaults={ + 'name': 'Graphic Era', + 'subdomain': 'graphicera', + 'email': 'info@gehu.ac.in', + 'phone': '+91XXXXXXXXXX', + 'address': 'Graphic Era Hill University, India', + 'is_active': True, + } + ) + if created: + self.stdout.write(self.style.SUCCESS(f'Created college: {college.name}')) + + # Create college admin + college_admin, created = User.objects.get_or_create( + email='admin@democollege.com', + defaults={ + 'first_name': 'College', + 'last_name': 'Admin', + 'role': User.Role.COLLEGE_ADMIN, + 'college': college, + } + ) + if created: + college_admin.set_password('admin123') + college_admin.save() + self.stdout.write(self.style.SUCCESS('Created college admin (admin@democollege.com / admin123)')) + + # Create department + dept, created = Department.objects.get_or_create( + college=college, + code='CS', + defaults={ + 'name': 'Computer Science', + 'hod': None, + } + ) + if created: + self.stdout.write(self.style.SUCCESS(f'Created department: {dept.name}')) + + # Create teachers + teachers_data = [ + {'email': 'teacher1@demo.com', 'name': 'John', 'surname': 'Doe', 'emp_id': 'T001', 'designation': 'Professor'}, + {'email': 'teacher2@demo.com', 'name': 'Jane', 'surname': 'Smith', 'emp_id': 'T002', 'designation': 'Associate Professor'}, + {'email': 'teacher3@demo.com', 'name': 'Bob', 'surname': 'Johnson', 'emp_id': 'T003', 'designation': 'Assistant Professor'}, + {'email': 'teacher4@demo.com', 'name': 'Alice', 'surname': 'Williams', 'emp_id': 'T004', 'designation': 'Professor'}, + ] + + teachers = [] + for t_data in teachers_data: + user, created = User.objects.get_or_create( + email=t_data['email'], + defaults={ + 'first_name': t_data['name'], + 'last_name': t_data['surname'], + 'role': User.Role.FACULTY, + 'college': college, + } + ) + if created: + user.set_password('teacher123') + user.save() + + teacher, created = Teacher.objects.get_or_create( + user=user, + defaults={ + 'employee_id': t_data['emp_id'], + 'department': dept, + 'designation': t_data['designation'], + 'max_weekly_hours': 20, + } + ) + teachers.append(teacher) + if created: + self.stdout.write(self.style.SUCCESS(f'Created teacher: {user.full_name}')) + + # Create subjects + subjects_data = [ + {'code': 'CS101', 'name': 'Introduction to Programming', 'credits': 3, 'quota': 3, 'is_lab': False}, + {'code': 'CS102', 'name': 'Data Structures', 'credits': 4, 'quota': 4, 'is_lab': False}, + {'code': 'CS103', 'name': 'Database Systems', 'credits': 3, 'quota': 3, 'is_lab': False}, + {'code': 'CS104', 'name': 'Programming Lab', 'credits': 2, 'quota': 2, 'is_lab': True}, + {'code': 'CS105', 'name': 'Web Development', 'credits': 3, 'quota': 3, 'is_lab': False}, + ] + + subjects = [] + for s_data in subjects_data: + subject, created = Subject.objects.get_or_create( + college=college, + code=s_data['code'], + defaults={ + 'name': s_data['name'], + 'credits': s_data['credits'], + 'weekly_quota': s_data['quota'], + 'is_lab': s_data['is_lab'], + 'department': dept, + } + ) + subjects.append(subject) + if created: + self.stdout.write(self.style.SUCCESS(f'Created subject: {subject.code}')) + + # Create sections + sections_data = [ + {'name': 'A', 'strength': 60}, + {'name': 'B', 'strength': 60}, + {'name': 'C', 'strength': 50}, + ] + + sections = [] + for s_data in sections_data: + section, created = Section.objects.get_or_create( + college=college, + name=s_data['name'], + year=1, + semester=1, + defaults={ + 'student_strength': s_data['strength'], + 'department': dept, + } + ) + sections.append(section) + if created: + self.stdout.write(self.style.SUCCESS(f'Created section: {section.name}')) + + # Create classrooms + classrooms_data = [ + {'name': 'R101', 'capacity': 80, 'is_lab': False}, + {'name': 'R102', 'capacity': 80, 'is_lab': False}, + {'name': 'R103', 'capacity': 60, 'is_lab': False}, + {'name': 'L101', 'capacity': 40, 'is_lab': True}, + {'name': 'L102', 'capacity': 40, 'is_lab': True}, + ] + + classrooms = [] + for c_data in classrooms_data: + classroom, created = Classroom.objects.get_or_create( + college=college, + name=c_data['name'], + defaults={ + 'capacity': c_data['capacity'], + 'is_lab': c_data['is_lab'], + 'department': dept, + } + ) + classrooms.append(classroom) + if created: + self.stdout.write(self.style.SUCCESS(f'Created classroom: {classroom.name}')) + + # Create subject-teacher mappings + mappings = [ + (subjects[0], teachers[0], sections[0]), # CS101 -> T001 -> Section A + (subjects[0], teachers[0], sections[1]), # CS101 -> T001 -> Section B + (subjects[1], teachers[1], sections[0]), # CS102 -> T002 -> Section A + (subjects[1], teachers[1], sections[1]), # CS102 -> T002 -> Section B + (subjects[2], teachers[2], sections[0]), # CS103 -> T003 -> Section A + (subjects[3], teachers[0], sections[0]), # CS104 -> T001 -> Section A + (subjects[4], teachers[3], sections[1]), # CS105 -> T004 -> Section B + ] + + for subject, teacher, section in mappings: + mapping, created = SubjectTeacherMapping.objects.get_or_create( + subject=subject, + teacher=teacher, + section=section, + defaults={'is_primary': True} + ) + if created: + self.stdout.write( + self.style.SUCCESS( + f'Created mapping: {subject.code} -> {teacher.employee_id} -> {section.name}' + ) + ) + + # Create teacher preferences + for teacher in teachers: + pref, created = TeacherPreference.objects.get_or_create( + teacher=teacher, + defaults={ + 'preferred_time_slots': [1, 2, 3, 4], # Morning slots + 'preferred_days': ['Monday', 'Tuesday', 'Wednesday', 'Thursday'], + 'unavailable_slots': [], + } + ) + if created: + self.stdout.write( + self.style.SUCCESS(f'Created preferences for {teacher.user.full_name}') + ) + + self.stdout.write(self.style.SUCCESS('\nSample data loaded successfully!')) + self.stdout.write(self.style.SUCCESS('\nLogin credentials:')) + self.stdout.write(self.style.SUCCESS('Master Admin: ayushkapri18@gmail.com / admin123')) + self.stdout.write(self.style.SUCCESS('College Admin: admin@democollege.com / admin123')) + self.stdout.write(self.style.SUCCESS('Teachers: teacher1@demo.com / teacher123 (and similar)')) diff --git a/backend/core/middleware.py b/backend/core/middleware.py new file mode 100644 index 0000000..b9b7205 --- /dev/null +++ b/backend/core/middleware.py @@ -0,0 +1,39 @@ +""" +Middleware for multi-tenant support. +""" +from django.http import Http404 +from core.models import College + + +class TenantMiddleware: + """Middleware to handle multi-tenancy based on subdomain or header.""" + + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + # Get tenant from subdomain or X-College-Code header + host = request.get_host().split(':')[0] + subdomain = host.split('.')[0] if '.' in host else None + + college = None + if subdomain and subdomain != 'www' and subdomain != 'admin': + try: + college = College.objects.get(subdomain=subdomain, is_active=True) + except College.DoesNotExist: + pass + + # Also check header for API requests + if not college: + college_code = request.headers.get('X-College-Code') + if college_code: + try: + college = College.objects.get(code=college_code, is_active=True) + except College.DoesNotExist: + pass + + # Attach college to request + request.college = college + + response = self.get_response(request) + return response diff --git a/backend/core/migrations/0001_initial.py b/backend/core/migrations/0001_initial.py new file mode 100644 index 0000000..f61ed87 --- /dev/null +++ b/backend/core/migrations/0001_initial.py @@ -0,0 +1,208 @@ +# Generated by Django 4.2.7 on 2026-01-28 06:14 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='User', + fields=[ + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('email', models.EmailField(max_length=254, unique=True)), + ('first_name', models.CharField(max_length=150)), + ('last_name', models.CharField(max_length=150)), + ('role', models.CharField(choices=[('master_admin', 'Master Admin'), ('college_admin', 'College Admin'), ('hod', 'Head of Department'), ('faculty', 'Faculty'), ('student', 'Student')], default='faculty', max_length=20)), + ('is_active', models.BooleanField(default=True)), + ('is_staff', models.BooleanField(default=False)), + ('is_superuser', models.BooleanField(default=False)), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now)), + ], + options={ + 'db_table': 'users', + 'ordering': ['-date_joined'], + }, + ), + migrations.CreateModel( + name='College', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255)), + ('code', models.CharField(max_length=50, unique=True)), + ('logo', models.ImageField(blank=True, null=True, upload_to='colleges/logos/')), + ('domain', models.CharField(blank=True, max_length=255, null=True, unique=True)), + ('subdomain', models.CharField(blank=True, max_length=100, null=True, unique=True)), + ('address', models.TextField(blank=True, null=True)), + ('phone', models.CharField(blank=True, max_length=20, null=True)), + ('email', models.EmailField(blank=True, max_length=254, null=True)), + ('is_active', models.BooleanField(default=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ], + options={ + 'db_table': 'colleges', + 'ordering': ['-created_at'], + }, + ), + migrations.CreateModel( + name='Department', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255)), + ('code', models.CharField(max_length=50)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('college', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='departments', to='core.college')), + ('hod', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='departments_headed', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'db_table': 'departments', + 'unique_together': {('college', 'code')}, + }, + ), + migrations.CreateModel( + name='Section', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=50)), + ('student_strength', models.IntegerField()), + ('year', models.IntegerField(blank=True, null=True)), + ('semester', models.IntegerField(blank=True, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('college', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sections', to='core.college')), + ('department', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sections', to='core.department')), + ], + options={ + 'db_table': 'sections', + 'unique_together': {('college', 'name', 'year', 'semester')}, + }, + ), + migrations.CreateModel( + name='Subject', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('code', models.CharField(max_length=50)), + ('name', models.CharField(max_length=255)), + ('credits', models.IntegerField()), + ('weekly_quota', models.IntegerField()), + ('is_lab', models.BooleanField(default=False)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('college', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subjects', to='core.college')), + ('department', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='subjects', to='core.department')), + ], + options={ + 'db_table': 'subjects', + 'unique_together': {('college', 'code')}, + }, + ), + migrations.CreateModel( + name='Teacher', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('employee_id', models.CharField(max_length=50)), + ('designation', models.CharField(max_length=100)), + ('max_weekly_hours', models.IntegerField(default=20)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('department', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='teachers', to='core.department')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='teacher_profile', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'db_table': 'teachers', + 'unique_together': {('department', 'employee_id')}, + }, + ), + migrations.CreateModel( + name='Timetable', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255)), + ('academic_year', models.CharField(max_length=20)), + ('semester', models.CharField(blank=True, max_length=20, null=True)), + ('status', models.CharField(choices=[('draft', 'Draft'), ('generated', 'Generated'), ('published', 'Published'), ('archived', 'Archived')], default='draft', max_length=20)), + ('timetable_data', models.JSONField()), + ('fitness_score', models.FloatField(blank=True, null=True)), + ('generation_config', models.JSONField(blank=True, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('college', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='timetables', to='core.college')), + ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_timetables', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'db_table': 'timetables', + 'ordering': ['-created_at'], + }, + ), + migrations.CreateModel( + name='TeacherPreference', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('preferred_time_slots', models.JSONField(default=list)), + ('preferred_days', models.JSONField(default=list)), + ('unavailable_slots', models.JSONField(default=list)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('teacher', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='preferences', to='core.teacher')), + ], + options={ + 'db_table': 'teacher_preferences', + }, + ), + migrations.AddField( + model_name='user', + name='college', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='users', to='core.college'), + ), + migrations.AddField( + model_name='user', + name='groups', + field=models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups'), + ), + migrations.AddField( + model_name='user', + name='user_permissions', + field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions'), + ), + migrations.CreateModel( + name='SubjectTeacherMapping', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('is_primary', models.BooleanField(default=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('section', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='subject_teacher_mappings', to='core.section')), + ('subject', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='teacher_mappings', to='core.subject')), + ('teacher', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subject_mappings', to='core.teacher')), + ], + options={ + 'db_table': 'subject_teacher_mappings', + 'unique_together': {('subject', 'teacher', 'section')}, + }, + ), + migrations.CreateModel( + name='Classroom', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=100)), + ('capacity', models.IntegerField()), + ('is_lab', models.BooleanField(default=False)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('college', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='classrooms', to='core.college')), + ('department', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='classrooms', to='core.department')), + ], + options={ + 'db_table': 'classrooms', + 'unique_together': {('college', 'name')}, + }, + ), + ] diff --git a/backend/core/migrations/__init__.py b/backend/core/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/core/models.py b/backend/core/models.py new file mode 100644 index 0000000..1171a49 --- /dev/null +++ b/backend/core/models.py @@ -0,0 +1,248 @@ +""" +Core models for multi-tenant timetable management system. +""" +from django.db import models +from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager +from django.utils import timezone +import uuid + + +class UserManager(BaseUserManager): + """Custom user manager.""" + + def create_user(self, email, password=None, **extra_fields): + if not email: + raise ValueError('The Email field must be set') + email = self.normalize_email(email) + user = self.model(email=email, **extra_fields) + user.set_password(password) + user.save(using=self._db) + return user + + def create_superuser(self, email, password=None, **extra_fields): + extra_fields.setdefault('is_staff', True) + extra_fields.setdefault('is_superuser', True) + extra_fields.setdefault('is_active', True) + extra_fields.setdefault('role', User.Role.MASTER_ADMIN) + + if extra_fields.get('is_staff') is not True: + raise ValueError('Superuser must have is_staff=True.') + if extra_fields.get('is_superuser') is not True: + raise ValueError('Superuser must have is_superuser=True.') + + return self.create_user(email, password, **extra_fields) + + +class College(models.Model): + """College/Institution model for multi-tenancy.""" + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + name = models.CharField(max_length=255) + code = models.CharField(max_length=50, unique=True) + logo = models.ImageField(upload_to='colleges/logos/', null=True, blank=True) + domain = models.CharField(max_length=255, unique=True, null=True, blank=True) + subdomain = models.CharField(max_length=100, unique=True, null=True, blank=True) + address = models.TextField(null=True, blank=True) + phone = models.CharField(max_length=20, null=True, blank=True) + email = models.EmailField(null=True, blank=True) + is_active = models.BooleanField(default=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + db_table = 'colleges' + ordering = ['-created_at'] + + def __str__(self): + return self.name + + +class User(AbstractBaseUser, PermissionsMixin): + """Custom user model with role-based access.""" + + class Role(models.TextChoices): + MASTER_ADMIN = 'master_admin', 'Master Admin' + COLLEGE_ADMIN = 'college_admin', 'College Admin' + HOD = 'hod', 'Head of Department' + FACULTY = 'faculty', 'Faculty' + STUDENT = 'student', 'Student' + + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + email = models.EmailField(unique=True) + first_name = models.CharField(max_length=150) + last_name = models.CharField(max_length=150) + role = models.CharField(max_length=20, choices=Role.choices, default=Role.FACULTY) + college = models.ForeignKey(College, on_delete=models.CASCADE, null=True, blank=True, related_name='users') + is_active = models.BooleanField(default=True) + is_staff = models.BooleanField(default=False) + is_superuser = models.BooleanField(default=False) + date_joined = models.DateTimeField(default=timezone.now) + + objects = UserManager() + + USERNAME_FIELD = 'email' + REQUIRED_FIELDS = ['first_name', 'last_name'] + + class Meta: + db_table = 'users' + ordering = ['-date_joined'] + + def __str__(self): + return f"{self.email} ({self.role})" + + @property + def full_name(self): + return f"{self.first_name} {self.last_name}" + + +class Department(models.Model): + """Department model.""" + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + college = models.ForeignKey(College, on_delete=models.CASCADE, related_name='departments') + name = models.CharField(max_length=255) + code = models.CharField(max_length=50) + hod = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='departments_headed') + created_at = models.DateTimeField(auto_now_add=True) + + class Meta: + db_table = 'departments' + unique_together = ['id'] + + def __str__(self): + return f"{self.name} ({self.college.name})" + + +class Teacher(models.Model): + """Teacher/Faculty model.""" + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='teacher_profile') + employee_id = models.CharField(max_length=50) + department = models.ForeignKey(Department, on_delete=models.CASCADE, related_name='teachers') + designation = models.CharField(max_length=100) + max_weekly_hours = models.IntegerField(default=20) + created_at = models.DateTimeField(auto_now_add=True) + + class Meta: + db_table = 'teachers' + unique_together = ['id'] + + def __str__(self): + return f"{self.user.full_name} ({self.employee_id})" + + +class Subject(models.Model): + """Subject/Course model.""" + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + college = models.ForeignKey(College, on_delete=models.CASCADE, related_name='subjects') + code = models.CharField(max_length=50) + name = models.CharField(max_length=255) + credits = models.IntegerField() + weekly_quota = models.IntegerField() # Number of classes per week + is_lab = models.BooleanField(default=False) + department = models.ForeignKey(Department, on_delete=models.CASCADE, related_name='subjects', null=True, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + + class Meta: + db_table = 'subjects' + unique_together = ['college', 'code'] + + def __str__(self): + return f"{self.code} - {self.name}" + + +class Section(models.Model): + """Section/Class model.""" + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + college = models.ForeignKey(College, on_delete=models.CASCADE, related_name='sections') + name = models.CharField(max_length=50) # e.g., "A", "B", "C" + student_strength = models.IntegerField() + department = models.ForeignKey(Department, on_delete=models.CASCADE, related_name='sections', null=True, blank=True) + year = models.IntegerField(null=True, blank=True) + semester = models.IntegerField(null=True, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + + class Meta: + db_table = 'sections' + unique_together = ['college', 'name', 'year', 'semester'] + + def __str__(self): + return f"{self.name} ({self.college.name})" + + +class Classroom(models.Model): + """Classroom model.""" + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + college = models.ForeignKey(College, on_delete=models.CASCADE, related_name='classrooms') + name = models.CharField(max_length=100) + capacity = models.IntegerField() + is_lab = models.BooleanField(default=False) + department = models.ForeignKey(Department, on_delete=models.SET_NULL, null=True, blank=True, related_name='classrooms') + created_at = models.DateTimeField(auto_now_add=True) + + class Meta: + db_table = 'classrooms' + unique_together = ['college', 'name'] + + def __str__(self): + return f"{self.name} ({self.college.name})" + + +class SubjectTeacherMapping(models.Model): + """Mapping between subjects and teachers.""" + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + subject = models.ForeignKey(Subject, on_delete=models.CASCADE, related_name='teacher_mappings') + teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE, related_name='subject_mappings') + section = models.ForeignKey(Section, on_delete=models.CASCADE, related_name='subject_teacher_mappings', null=True, blank=True) + is_primary = models.BooleanField(default=True) # Primary teacher for this subject + created_at = models.DateTimeField(auto_now_add=True) + + class Meta: + db_table = 'subject_teacher_mappings' + unique_together = ['subject', 'teacher', 'section'] + + def __str__(self): + return f"{self.subject.code} - {self.teacher.user.full_name}" + + +class Timetable(models.Model): + """Generated timetable model.""" + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + college = models.ForeignKey(College, on_delete=models.CASCADE, related_name='timetables') + name = models.CharField(max_length=255) + academic_year = models.CharField(max_length=20) + semester = models.CharField(max_length=20, null=True, blank=True) + status = models.CharField(max_length=20, choices=[ + ('draft', 'Draft'), + ('generated', 'Generated'), + ('published', 'Published'), + ('archived', 'Archived'), + ], default='draft') + timetable_data = models.JSONField() # Stores the generated timetable + fitness_score = models.FloatField(null=True, blank=True) + generation_config = models.JSONField(null=True, blank=True) # Stores algorithm config + created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='created_timetables') + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + db_table = 'timetables' + ordering = ['-created_at'] + + def __str__(self): + return f"{self.name} ({self.college.name})" + + +class TeacherPreference(models.Model): + """Teacher preferences for time slots.""" + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE, related_name='preferences') + preferred_time_slots = models.JSONField(default=list) # List of preferred time slot IDs + preferred_days = models.JSONField(default=list) # List of preferred days + unavailable_slots = models.JSONField(default=list) # List of unavailable time slots + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + db_table = 'teacher_preferences' + + def __str__(self): + return f"Preferences for {self.teacher.user.full_name}" diff --git a/backend/core/permissions.py b/backend/core/permissions.py new file mode 100644 index 0000000..69b9f89 --- /dev/null +++ b/backend/core/permissions.py @@ -0,0 +1,52 @@ +""" +Custom permissions for the timetable system. +""" +from rest_framework import permissions + + +class IsMasterAdmin(permissions.BasePermission): + """Permission check for Master Admin.""" + def has_permission(self, request, view): + return ( + request.user and + request.user.is_authenticated and + request.user.role == 'master_admin' + ) + + +class IsCollegeAdminOrMaster(permissions.BasePermission): + """Permission check for College Admin or Master Admin.""" + def has_permission(self, request, view): + return ( + request.user and + request.user.is_authenticated and + request.user.role in ['master_admin', 'college_admin'] + ) + + +class IsCollegeMember(permissions.BasePermission): + """Permission check for any college member.""" + def has_permission(self, request, view): + return ( + request.user and + request.user.is_authenticated and + request.user.college is not None + ) + + +class IsOwnerOrAdmin(permissions.BasePermission): + """Permission to allow owners or admins.""" + def has_object_permission(self, request, view, obj): + # Master admin can do anything + if request.user.role == 'master_admin': + return True + + # College admin can manage their college's resources + if request.user.role == 'college_admin' and hasattr(obj, 'college'): + return obj.college == request.user.college + + # Users can manage their own resources + if hasattr(obj, 'created_by'): + return obj.created_by == request.user + + return False diff --git a/backend/core/serializers.py b/backend/core/serializers.py new file mode 100644 index 0000000..5073337 --- /dev/null +++ b/backend/core/serializers.py @@ -0,0 +1,143 @@ +""" +Serializers for core models. +""" +from rest_framework import serializers +from core.models import ( + User, College, Department, Teacher, Subject, Section, + Classroom, SubjectTeacherMapping, Timetable, TeacherPreference +) + + +class CollegeSerializer(serializers.ModelSerializer): + """Serializer for College model.""" + class Meta: + model = College + fields = ['id', 'name', 'code', 'logo', 'domain', 'subdomain', + 'address', 'phone', 'email', 'is_active', 'created_at', 'updated_at'] + read_only_fields = ['id', 'created_at', 'updated_at'] + + +class UserSerializer(serializers.ModelSerializer): + """Serializer for User model.""" + full_name = serializers.ReadOnlyField() + college_name = serializers.CharField(source='college.name', read_only=True) + + class Meta: + model = User + fields = ['id', 'email', 'first_name', 'last_name', 'full_name', + 'role', 'college', 'college_name', 'is_active', 'date_joined'] + read_only_fields = ['id', 'date_joined'] + + +class DepartmentSerializer(serializers.ModelSerializer): + """Serializer for Department model.""" + college_name = serializers.CharField(source='college.name', read_only=True) + hod_name = serializers.CharField(source='hod.full_name', read_only=True) + + class Meta: + model = Department + fields = ['id', 'college', 'college_name', 'name', 'code', 'hod', 'hod_name', 'created_at'] + read_only_fields = ['id', 'created_at', 'college'] + + +class TeacherSerializer(serializers.ModelSerializer): + """Serializer for Teacher model.""" + user_email = serializers.CharField(source='user.email', read_only=True) + user_name = serializers.CharField(source='user.full_name', read_only=True) + department_name = serializers.CharField(source='department.name', read_only=True) + + class Meta: + model = Teacher + fields = ['id', 'user', 'user_email', 'user_name', 'employee_id', + 'department', 'department_name', 'designation', 'max_weekly_hours', 'created_at'] + read_only_fields = ['id', 'created_at'] + + +class SubjectSerializer(serializers.ModelSerializer): + """Serializer for Subject model.""" + college_name = serializers.CharField(source='college.name', read_only=True) + department_name = serializers.CharField(source='department.name', read_only=True) + + class Meta: + model = Subject + fields = ['id', 'college', 'college_name', 'code', 'name', 'credits', + 'weekly_quota', 'is_lab', 'department', 'department_name', 'created_at'] + read_only_fields = ['id', 'created_at', 'college'] + + +class SectionSerializer(serializers.ModelSerializer): + """Serializer for Section model.""" + college_name = serializers.CharField(source='college.name', read_only=True) + department_name = serializers.CharField(source='department.name', read_only=True) + + class Meta: + model = Section + fields = ['id', 'college', 'college_name', 'name', 'student_strength', + 'department', 'department_name', 'year', 'semester', 'created_at'] + read_only_fields = ['id', 'created_at', 'college'] + + +class ClassroomSerializer(serializers.ModelSerializer): + """Serializer for Classroom model.""" + college_name = serializers.CharField(source='college.name', read_only=True) + department_name = serializers.CharField(source='department.name', read_only=True) + + class Meta: + model = Classroom + fields = ['id', 'college', 'college_name', 'name', 'capacity', + 'is_lab', 'department', 'department_name', 'created_at'] + read_only_fields = ['id', 'created_at', 'college'] + + +class SubjectTeacherMappingSerializer(serializers.ModelSerializer): + """Serializer for SubjectTeacherMapping model.""" + subject_code = serializers.CharField(source='subject.code', read_only=True) + subject_name = serializers.CharField(source='subject.name', read_only=True) + teacher_name = serializers.CharField(source='teacher.user.full_name', read_only=True) + section_name = serializers.CharField(source='section.name', read_only=True) + + class Meta: + model = SubjectTeacherMapping + fields = ['id', 'subject', 'subject_code', 'subject_name', 'teacher', + 'teacher_name', 'section', 'section_name', 'is_primary', 'created_at'] + read_only_fields = ['id', 'created_at'] + + +class TimetableSerializer(serializers.ModelSerializer): + """Serializer for Timetable model.""" + college_name = serializers.CharField(source='college.name', read_only=True) + created_by_name = serializers.CharField(source='created_by.full_name', read_only=True) + + class Meta: + model = Timetable + fields = ['id', 'college', 'college_name', 'name', 'academic_year', + 'semester', 'status', 'timetable_data', 'fitness_score', + 'generation_config', 'created_by', 'created_by_name', + 'created_at', 'updated_at'] + read_only_fields = ['id', 'created_at', 'updated_at', 'fitness_score', 'college'] + + +class TeacherPreferenceSerializer(serializers.ModelSerializer): + """Serializer for TeacherPreference model.""" + teacher_name = serializers.CharField(source='teacher.user.full_name', read_only=True) + + class Meta: + model = TeacherPreference + fields = ['id', 'teacher', 'teacher_name', 'preferred_time_slots', + 'preferred_days', 'unavailable_slots', 'created_at', 'updated_at'] + read_only_fields = ['id', 'created_at', 'updated_at'] + + +class GenerateTimetableSerializer(serializers.Serializer): + """Serializer for timetable generation request.""" + name = serializers.CharField(max_length=255) + academic_year = serializers.CharField(max_length=20) + semester = serializers.CharField(max_length=20, required=False, allow_blank=True) + total_generations = serializers.IntegerField(default=50, min_value=1, max_value=200) + + # Optional overrides + time_slots = serializers.DictField(required=False) + day_map = serializers.DictField(required=False) + time_slot_map = serializers.DictField(required=False) + working_days = serializers.ListField(child=serializers.CharField(), required=False) + fixed_teacher_assignment = serializers.DictField(required=False) diff --git a/backend/core/urls.py b/backend/core/urls.py new file mode 100644 index 0000000..ef4621a --- /dev/null +++ b/backend/core/urls.py @@ -0,0 +1,22 @@ +""" +URLs for core app. +""" +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from core import views + +router = DefaultRouter() +router.register(r'colleges', views.CollegeViewSet, basename='college') +router.register(r'departments', views.DepartmentViewSet, basename='department') +router.register(r'teachers', views.TeacherViewSet, basename='teacher') +router.register(r'subjects', views.SubjectViewSet, basename='subject') +router.register(r'sections', views.SectionViewSet, basename='section') +router.register(r'classrooms', views.ClassroomViewSet, basename='classroom') +router.register(r'subject-teacher-mappings', views.SubjectTeacherMappingViewSet, basename='subject-teacher-mapping') +router.register(r'timetables', views.TimetableViewSet, basename='timetable') +router.register(r'teacher-preferences', views.TeacherPreferenceViewSet, basename='teacher-preference') +router.register(r'generate-timetable', views.GenerateTimetableViewSet, basename='generate-timetable') + +urlpatterns = [ + path('', include(router.urls)), +] diff --git a/backend/core/views.py b/backend/core/views.py new file mode 100644 index 0000000..0b00ab2 --- /dev/null +++ b/backend/core/views.py @@ -0,0 +1,359 @@ +""" +Views for core models. +""" +from rest_framework import viewsets, status, generics +from rest_framework.decorators import action +from rest_framework.response import Response +from rest_framework.permissions import IsAuthenticated +from django.db.models import Q +from core.models import ( + College, Department, Teacher, Subject, Section, + Classroom, SubjectTeacherMapping, Timetable, TeacherPreference +) +from core.serializers import ( + CollegeSerializer, DepartmentSerializer, TeacherSerializer, + SubjectSerializer, SectionSerializer, ClassroomSerializer, + SubjectTeacherMappingSerializer, TimetableSerializer, + TeacherPreferenceSerializer, GenerateTimetableSerializer +) +from core.permissions import ( + IsMasterAdmin, IsCollegeAdminOrMaster, IsCollegeMember, IsOwnerOrAdmin +) +from algorithm import run_timetable_generation +from algorithm.helpers import initialize_teacher_availability +import logging + +logger = logging.getLogger(__name__) + + +class CollegeViewSet(viewsets.ModelViewSet): + """ViewSet for College management.""" + queryset = College.objects.all() + serializer_class = CollegeSerializer + permission_classes = [IsAuthenticated, IsMasterAdmin] + filterset_fields = ['is_active'] + search_fields = ['name', 'code', 'subdomain'] + + +class DepartmentViewSet(viewsets.ModelViewSet): + """ViewSet for Department management.""" + serializer_class = DepartmentSerializer + permission_classes = [IsAuthenticated, IsCollegeMember] + + def get_queryset(self): + user = self.request.user + if user.role == 'master_admin': + return Department.objects.all() + return Department.objects.filter(college=user.college) + + def perform_create(self, serializer): + if not self.request.user.college: + raise ValidationError("User has no college assigned") + + serializer.save(college=self.request.user.college) + + +class TeacherViewSet(viewsets.ModelViewSet): + """ViewSet for Teacher management.""" + serializer_class = TeacherSerializer + permission_classes = [IsAuthenticated, IsCollegeMember] + search_fields = ['user__email', 'user__first_name', 'user__last_name', 'employee_id'] + + def get_queryset(self): + user = self.request.user + if user.role == 'master_admin': + return Teacher.objects.all() + return Teacher.objects.filter(department__college=user.college) + + +class SubjectViewSet(viewsets.ModelViewSet): + """ViewSet for Subject management.""" + serializer_class = SubjectSerializer + permission_classes = [IsAuthenticated, IsCollegeMember] + search_fields = ['code', 'name'] + filterset_fields = ['is_lab', 'department'] + + def get_queryset(self): + user = self.request.user + if user.role == 'master_admin': + return Subject.objects.all() + return Subject.objects.filter(college=user.college) + + def perform_create(self, serializer): + + if not self.request.user.college: + raise ValidationError("User has no college assigned") + serializer.save(college=self.request.user.college) + + +class SectionViewSet(viewsets.ModelViewSet): + """ViewSet for Section management.""" + serializer_class = SectionSerializer + permission_classes = [IsAuthenticated, IsCollegeMember] + filterset_fields = ['year', 'semester'] + + def get_queryset(self): + user = self.request.user + if user.role == 'master_admin': + return Section.objects.all() + return Section.objects.filter(college=user.college) + + def perform_create(self, serializer): + if not self.request.user.college: + raise ValidationError("User has no college assigned") + serializer.save(college=self.request.user.college) + + +class ClassroomViewSet(viewsets.ModelViewSet): + """ViewSet for Classroom management.""" + serializer_class = ClassroomSerializer + permission_classes = [IsAuthenticated, IsCollegeMember] + filterset_fields = ['is_lab'] + search_fields = ['name'] + + def get_queryset(self): + user = self.request.user + if user.role == 'master_admin': + return Classroom.objects.all() + return Classroom.objects.filter(college=user.college) + + def perform_create(self, serializer): + if not self.request.user.college: + raise ValidationError("User has no college assigned") + serializer.save(college=self.request.user.college) + + +class SubjectTeacherMappingViewSet(viewsets.ModelViewSet): + """ViewSet for Subject-Teacher mapping.""" + serializer_class = SubjectTeacherMappingSerializer + permission_classes = [IsAuthenticated, IsCollegeMember] + filterset_fields = ['is_primary', 'subject', 'teacher', 'section'] + + def get_queryset(self): + user = self.request.user + if user.role == 'master_admin': + return SubjectTeacherMapping.objects.all() + return SubjectTeacherMapping.objects.filter( + subject__college=user.college + ) + + +class TeacherPreferenceViewSet(viewsets.ModelViewSet): + """ViewSet for Teacher Preference management.""" + serializer_class = TeacherPreferenceSerializer + permission_classes = [IsAuthenticated, IsCollegeMember] + + def get_queryset(self): + user = self.request.user + if user.role == 'master_admin': + return TeacherPreference.objects.all() + return TeacherPreference.objects.filter( + teacher__department__college=user.college + ) + + +class TimetableViewSet(viewsets.ModelViewSet): + """ViewSet for Timetable management.""" + serializer_class = TimetableSerializer + permission_classes = [IsAuthenticated, IsCollegeMember] + filterset_fields = ['status', 'academic_year', 'semester'] + search_fields = ['name', 'academic_year'] + + def get_queryset(self): + user = self.request.user + if user.role == 'master_admin': + return Timetable.objects.all() + return Timetable.objects.filter(college=user.college) + + def perform_create(self, serializer): + if not self.request.user.college: + raise ValidationError("User has no college assigned") + + serializer.save( + college=self.request.user.college, + created_by=self.request.user + ) + + +class GenerateTimetableViewSet(viewsets.ViewSet): + """ViewSet for generating timetables.""" + permission_classes = [IsAuthenticated, IsCollegeAdminOrMaster] + + @action(detail=False, methods=['post']) + def generate(self, request): + """Generate a timetable using the genetic algorithm.""" + serializer = GenerateTimetableSerializer(data=request.data) + if not serializer.is_valid(): + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + college = request.user.college + if not college: + return Response( + {'error': 'User must be associated with a college'}, + status=status.HTTP_400_BAD_REQUEST + ) + + try: + # Get all required data from database + sections = Section.objects.filter(college=college) + classrooms = Classroom.objects.filter(college=college, is_lab=False) + labs = Classroom.objects.filter(college=college, is_lab=True) + subjects = Subject.objects.filter(college=college) + teachers = Teacher.objects.filter(department__college=college) + mappings = SubjectTeacherMapping.objects.filter(subject__college=college) + + # Validate resources exist + if not sections.exists(): + return Response( + {'error': 'No sections found. Please add sections first.'}, + status=status.HTTP_400_BAD_REQUEST + ) + if not teachers.exists(): + return Response( + {'error': 'No teachers found. Please add teachers first.'}, + status=status.HTTP_400_BAD_REQUEST + ) + if not subjects.exists(): + return Response( + {'error': 'No subjects found. Please add subjects first.'}, + status=status.HTTP_400_BAD_REQUEST + ) + if not classrooms.exists() and not labs.exists(): + return Response( + {'error': 'No classrooms or labs found. Please add classrooms/labs first.'}, + status=status.HTTP_400_BAD_REQUEST + ) + if not mappings.exists(): + return Response( + {'error': 'No subject-teacher mappings found. Please create mappings first.'}, + status=status.HTTP_400_BAD_REQUEST + ) + + # Build data structures for algorithm + total_sections = {s.name: s.student_strength for s in sections} + total_classrooms = {c.name: c.capacity for c in classrooms} + total_labs = {l.name: l.capacity for l in labs} + + # Build teacher-subject mapping + teacher_subject_mapping = {} + for mapping in mappings: + subject_code = mapping.subject.code + teacher_id = mapping.teacher.employee_id + if subject_code not in teacher_subject_mapping: + teacher_subject_mapping[subject_code] = [] + teacher_subject_mapping[subject_code].append(teacher_id) + + # Build subject quota limits + subject_quota_limits = {s.code: s.weekly_quota for s in subjects} + + # Build lab subjects list + lab_subjects = [s.code for s in subjects if s.is_lab] + + # Build teacher preferences + teacher_preferences = {} + teacher_weekly_workload = {} + teacher_duty_days = {} + + for teacher in teachers: + teacher_id = teacher.employee_id + teacher_weekly_workload[teacher_id] = teacher.max_weekly_hours + teacher_duty_days[teacher_id] = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'] + + # Get preferences if exists + try: + pref = TeacherPreference.objects.get(teacher=teacher) + teacher_preferences[teacher_id] = pref.preferred_time_slots or [] + except TeacherPreference.DoesNotExist: + teacher_preferences[teacher_id] = [] + + # Initialize availability matrices + teacher_list = list(teacher_weekly_workload.keys()) + num_days = len(serializer.validated_data.get('working_days', ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'])) + num_slots = 7 # Default + + teacher_availability_matrix = initialize_teacher_availability(teacher_list, num_days, num_slots) + + lab_availability_matrix = {} + for lab in labs: + lab_availability_matrix[lab.name] = [[True] * num_slots for _ in range(num_days)] + + # Default time slots + time_slots = serializer.validated_data.get('time_slots', { + 1: "9:00 - 9:55", + 2: "9:55 - 10:50", + 3: "11:10 - 12:05", + 4: "12:05 - 1:00", + 5: "1:20 - 2:15", + 6: "2:15 - 3:10", + 7: "3:30 - 4:25", + }) + + day_map = serializer.validated_data.get('day_map', { + "Monday": 0, + "Tuesday": 1, + "Wednesday": 2, + "Thursday": 3, + "Friday": 4, + }) + + time_slot_map = serializer.validated_data.get('time_slot_map', { + "9:00 - 9:55": 1, + "9:55 - 10:50": 2, + "11:10 - 12:05": 3, + "12:05 - 1:00": 4, + "1:20 - 2:15": 5, + "2:15 - 3:10": 6, + "3:30 - 4:25": 7, + }) + + working_days = serializer.validated_data.get('working_days', ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']) + + # Run algorithm + best_tt, final_teacher, final_lab, fitness_score = run_timetable_generation( + teacher_subject_mapping=teacher_subject_mapping, + total_sections=total_sections, + total_classrooms=total_classrooms, + total_labs=total_labs, + teacher_preferences=teacher_preferences, + teacher_weekly_workload=teacher_weekly_workload, + special_subjects={}, + labs=lab_subjects, + subject_quota_limits=subject_quota_limits, + teacher_duty_days=teacher_duty_days, + teacher_availability_matrix=teacher_availability_matrix, + lab_availability_matrix=lab_availability_matrix, + total_generations=serializer.validated_data['total_generations'], + time_slots=time_slots, + day_map=day_map, + time_slot_map=time_slot_map, + fixed_teacher_assignment=serializer.validated_data.get('fixed_teacher_assignment'), + working_days=working_days, + ) + + # Save timetable + timetable = Timetable.objects.create( + college=college, + name=serializer.validated_data['name'], + academic_year=serializer.validated_data['academic_year'], + semester=serializer.validated_data.get('semester', ''), + status='generated', + timetable_data=best_tt, + fitness_score=fitness_score, + generation_config=serializer.validated_data, + created_by=request.user, + ) + + return Response({ + 'timetable_id': str(timetable.id), + 'timetable': best_tt, + 'fitness_score': fitness_score, + 'teacher_availability': final_teacher, + 'lab_availability': final_lab, + }, status=status.HTTP_201_CREATED) + + except Exception as e: + logger.error(f"Error generating timetable: {str(e)}", exc_info=True) + return Response( + {'error': f'Failed to generate timetable: {str(e)}'}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR + ) diff --git a/backend/manage.py b/backend/manage.py new file mode 100644 index 0000000..2294b65 --- /dev/null +++ b/backend/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'timetable_backend.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..2342018 --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,11 @@ +Django==4.2.7 +djangorestframework==3.14.0 +django-cors-headers==4.3.1 +djangorestframework-simplejwt==5.3.0 +psycopg2-binary==2.9.9 +python-decouple==3.8 +Pillow==10.1.0 +celery==5.3.4 +redis==5.0.1 +django-filter==23.5 +drf-spectacular==0.26.5 diff --git a/backend/timetable_backend/__init__.py b/backend/timetable_backend/__init__.py new file mode 100644 index 0000000..15d7c50 --- /dev/null +++ b/backend/timetable_backend/__init__.py @@ -0,0 +1,5 @@ +# This will make sure the app is always imported when +# Django starts so that shared_task will use this app. +from .celery import app as celery_app + +__all__ = ('celery_app',) diff --git a/backend/timetable_backend/celery.py b/backend/timetable_backend/celery.py new file mode 100644 index 0000000..0b4bde2 --- /dev/null +++ b/backend/timetable_backend/celery.py @@ -0,0 +1,11 @@ +""" +Celery configuration for timetable_backend project. +""" +import os +from celery import Celery + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'timetable_backend.settings') + +app = Celery('timetable_backend') +app.config_from_object('django.conf:settings', namespace='CELERY') +app.autodiscover_tasks() diff --git a/backend/timetable_backend/settings.py b/backend/timetable_backend/settings.py new file mode 100644 index 0000000..3e07abc --- /dev/null +++ b/backend/timetable_backend/settings.py @@ -0,0 +1,171 @@ +""" +Django settings for timetable_backend project. +""" +import os +from pathlib import Path +from decouple import config +from datetime import timedelta + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = config('SECRET_KEY', default='django-insecure-change-this-in-production') + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = config('DEBUG', default=True, cast=bool) + +ALLOWED_HOSTS = config( + 'ALLOWED_HOSTS', + default='localhost,127.0.0.1,172.16.4.55', + cast=lambda v: [s.strip() for s in v.split(',')] +) +# Application definition +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'rest_framework', + 'rest_framework_simplejwt', + 'corsheaders', + 'drf_spectacular', + 'django_filters', + 'core', + 'algorithm', + 'api', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'corsheaders.middleware.CorsMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'core.middleware.TenantMiddleware', +] + +ROOT_URLCONF = 'timetable_backend.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'timetable_backend.wsgi.application' + +# Database +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': config('DB_NAME', default='timetable_db'), + 'USER': config('DB_USER', default='postgres'), + 'PASSWORD': config('DB_PASSWORD', default='postgres'), + 'HOST': config('DB_HOST', default='localhost'), + 'PORT': config('DB_PORT', default='5432'), + } +} + +# Password validation +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + +# Internationalization +LANGUAGE_CODE = 'en-us' +TIME_ZONE = 'UTC' +USE_I18N = True +USE_TZ = True + +# Static files (CSS, JavaScript, Images) +STATIC_URL = '/static/' +STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') + +MEDIA_URL = '/media/' +MEDIA_ROOT = os.path.join(BASE_DIR, 'media') + +# Default primary key field type +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +# Custom User Model +AUTH_USER_MODEL = 'core.User' + +# REST Framework +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework_simplejwt.authentication.JWTAuthentication', + ), + 'DEFAULT_PERMISSION_CLASSES': ( + 'rest_framework.permissions.IsAuthenticated', + ), + 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', + 'PAGE_SIZE': 20, + 'DEFAULT_FILTER_BACKENDS': ( + 'django_filters.rest_framework.DjangoFilterBackend', + 'rest_framework.filters.SearchFilter', + 'rest_framework.filters.OrderingFilter', + ), + 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', +} + +# JWT Settings +SIMPLE_JWT = { + 'ACCESS_TOKEN_LIFETIME': timedelta(hours=24), + 'REFRESH_TOKEN_LIFETIME': timedelta(days=7), + 'ROTATE_REFRESH_TOKENS': True, + 'BLACKLIST_AFTER_ROTATION': True, + 'ALGORITHM': 'HS256', + 'SIGNING_KEY': SECRET_KEY, + 'AUTH_HEADER_TYPES': ('Bearer',), +} + +# CORS Settings +CORS_ALLOWED_ORIGINS = config( + 'CORS_ALLOWED_ORIGINS', + default='http://localhost:3000,http://localhost:5173,http://172.16.4.55:3000', + cast=lambda v: [s.strip() for s in v.split(',')] +) +CORS_ALLOW_CREDENTIALS = True + +# Spectacular Settings (API Documentation) +SPECTACULAR_SETTINGS = { + 'TITLE': 'Timetable Management API', + 'DESCRIPTION': 'API for managing timetables with genetic algorithm optimization', + 'VERSION': '1.0.0', + 'SERVE_INCLUDE_SCHEMA': False, +} + +# Celery Configuration (for async tasks) +CELERY_BROKER_URL = config('CELERY_BROKER_URL', default='redis://localhost:6379/0') +CELERY_RESULT_BACKEND = config('CELERY_RESULT_BACKEND', default='redis://localhost:6379/0') +CELERY_ACCEPT_CONTENT = ['json'] +CELERY_TASK_SERIALIZER = 'json' +CELERY_RESULT_SERIALIZER = 'json' +CELERY_TIMEZONE = TIME_ZONE diff --git a/backend/timetable_backend/urls.py b/backend/timetable_backend/urls.py new file mode 100644 index 0000000..8b46977 --- /dev/null +++ b/backend/timetable_backend/urls.py @@ -0,0 +1,20 @@ +""" +URL configuration for timetable_backend project. +""" +from django.contrib import admin +from django.urls import path, include +from django.conf import settings +from django.conf.urls.static import static +from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView + +urlpatterns = [ + path('admin/', admin.site.urls), + path('api/schema/', SpectacularAPIView.as_view(), name='schema'), + path('api/docs/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'), + path('api/auth/', include('api.urls')), + path('api/', include('core.urls')), +] + +if settings.DEBUG: + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/backend/timetable_backend/wsgi.py b/backend/timetable_backend/wsgi.py new file mode 100644 index 0000000..0c03338 --- /dev/null +++ b/backend/timetable_backend/wsgi.py @@ -0,0 +1,10 @@ +""" +WSGI config for timetable_backend project. +""" +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'timetable_backend.settings') + +application = get_wsgi_application() diff --git a/build/lib/Constants/__init__.py b/build/lib/Constants/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/Constants/classroom_allocate.py b/build/lib/Constants/classroom_allocate.py new file mode 100644 index 0000000..5030f56 --- /dev/null +++ b/build/lib/Constants/classroom_allocate.py @@ -0,0 +1,56 @@ +from ortools.sat.python import cp_model + + +def assign_section_to_classes(x_section, y_classrooms, z_time_slots): + # Create a CP-SAT model + model = cp_model.CpModel() + + # Decision variables + classrooms = [ + model.NewIntVar(0, y_classrooms - 1, f"classroom_{i}") for i in range(x_section) + ] + timeslots = [ + model.NewIntVar(0, z_time_slots - 1, f"timeslot_{i}") for i in range(x_section) + ] + + # Constraints + # 1. No two section can share the same classroom and time slot + for i in range(x_section): + for j in range(i + 1, x_section): + # Define a Boolean variable for the condition (timeslots[i] == timeslots[j]) + same_timeslot = model.NewBoolVar(f"same_timeslot_{i}_{j}") + model.Add(timeslots[i] == timeslots[j]).OnlyEnforceIf(same_timeslot) + model.Add(timeslots[i] != timeslots[j]).OnlyEnforceIf(same_timeslot.Not()) + + # Enforce classroom difference if the timeslot is the same + model.Add(classrooms[i] != classrooms[j]).OnlyEnforceIf(same_timeslot) + + # Solver + solver = cp_model.CpSolver() + status = solver.Solve(model) + + # Check results + if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE: + for i in range(x_section): + assigned_classroom = solver.Value(classrooms[i]) + assigned_timeslot = solver.Value(timeslots[i]) + print( + f"Subject {i + 1} -> Classroom {assigned_classroom}, Timeslot {assigned_timeslot}" + ) + else: + print("No feasible solution found.") + + +from datetime import datetime + +start_time = datetime.now() + +X = 80 # Number of section +Y = 50 # Number of classrooms +Z = 7 # Number of time slots + +assign_section_to_classes(X, Y, Z) + +end_time = datetime.now() + +print(end_time - start_time) diff --git a/build/lib/Constants/classroom_tt.py b/build/lib/Constants/classroom_tt.py new file mode 100644 index 0000000..262c3fe --- /dev/null +++ b/build/lib/Constants/classroom_tt.py @@ -0,0 +1,48 @@ +import json +from Samples.samples import (WorkingDays, SampleChromosome) + +class ClassroomTimetable: + def __init__(self): + # Dictionary to hold timetables for each classroom + self.classroom_timetable = {} + + def generate_classroom_timetable(self, chromosome): + for week, days in chromosome.items(): + for day, sections in days.items(): + for section, classes in sections.items(): + for entry in classes: + classroom_id = entry["classroom_id"] + + if classroom_id not in self.classroom_timetable: + self.classroom_timetable[classroom_id] = {day: {} for day in WorkingDays.days} + + if section not in self.classroom_timetable[classroom_id][day]: + self.classroom_timetable[classroom_id][day][section] = [] + + self.classroom_timetable[classroom_id][day][section].append({ + "subject_id": entry["subject_id"], + "teacher_id": entry["teacher_id"], + "time_slot": entry["time_slot"], + }) + + return self.classroom_timetable + + def save_timetable_to_json(self, file_path="Constants/classroom_timetable.json"): + try: + with open(file_path, "w") as json_file: + json.dump(self.classroom_timetable, json_file, indent=4) + # print(f"Timetable successfully saved to '{file_path}'.") + except Exception as e: + print(f"Error saving timetable to '{file_path}': {e}") + +if __name__ == "__main__": + # Generate classroom-wise timetable + classroom_timetable = ClassroomTimetable() + w = { + "Week 2": SampleChromosome.schedule2, + "Week 1": SampleChromosome.schedule1 + } + classroom_tt = classroom_timetable.generate_classroom_timetable(w) + print(classroom_tt) + # Save as JSON + classroom_timetable.save_timetable_to_json() diff --git a/build/lib/Constants/classroom_tt_csv.py b/build/lib/Constants/classroom_tt_csv.py new file mode 100644 index 0000000..6b59a1f --- /dev/null +++ b/build/lib/Constants/classroom_tt_csv.py @@ -0,0 +1,83 @@ +import os +import csv +from Samples.samples import (SampleChromosome,WorkingDays) +from Constants.classroom_tt import ClassroomTimetable # Ensure correct import + +# Predefined time slots mapping +time_slots = { + 1: "08:00 - 09:00", + 2: "09:00 - 10:00", + 3: "11:00 - 12:00", + 4: "12:00 - 13:00", + 5: "16:50 - 17:50", +} + +# Reverse lookup to get slot number from time string +time_slot_order = {v: k for k, v in time_slots.items()} + +# List of days in the correct order +days_of_week_order =WorkingDays.days + +def extract_time_slots(timetable): + """Extract and sort time slots from timetable.""" + unique_slots = set() + + # Collect all unique time slots + for day_data in timetable.values(): + for slots in day_data.values(): + for entry in slots: + if isinstance(entry, dict) and "time_slot" in entry: # ✅ Ensure correct structure + unique_slots.add(entry["time_slot"]) + else: + print(f"⚠️ ERROR: Unexpected entry format -> {entry}") + + # Sort slots based on predefined order + sorted_slots = sorted(unique_slots, key=lambda slot: time_slot_order.get(slot, float('inf'))) + + return [("CLASS", slot) for slot in sorted_slots] # Only include class slots + +def classroom_json_to_csv(classroom_timetable, output_folder): + """Convert classroom timetable JSON to CSV.""" + os.makedirs(output_folder, exist_ok=True) + + for classroom_id, schedule in classroom_timetable.items(): + final_slots = extract_time_slots(schedule) + csv_file = os.path.join(output_folder, f"{classroom_id}.csv") + header = ["DAY"] + [slot[1] for slot in final_slots] + rows = [] + + # Sort days correctly + sorted_days = sorted(schedule.items(), key=lambda x: days_of_week_order.index(x[0])) + + for day, day_data in sorted_days: + row = [day] + ["" for _ in final_slots] # Empty slots initially + + for section, classes in day_data.items(): + for entry in classes: + for i, slot in enumerate(final_slots): + if entry["time_slot"] == slot[1]: + row[i + 1] = f"{entry['subject_id']} ({section}, {entry['teacher_id']})" + + rows.append(row) + + # Write to CSV + with open(csv_file, mode="w", newline="", encoding="utf-8") as file: + writer = csv.writer(file) + writer.writerow(header) + writer.writerows(rows) + +if __name__ == "__main__": + # Generate classroom-wise timetable + classroom_timetable = ClassroomTimetable() + weekly_schedule = { + "Week 2": SampleChromosome.schedule2, + "Week 1": SampleChromosome.schedule1 + } + + classroom_tt = classroom_timetable.generate_classroom_timetable(weekly_schedule) + + # Output folder for CSV files + output_folder = "output_csvs" + + # Convert the generated timetable to CSV + classroom_json_to_csv(classroom_tt, output_folder) diff --git a/build/lib/Constants/constant.py b/build/lib/Constants/constant.py new file mode 100644 index 0000000..6960403 --- /dev/null +++ b/build/lib/Constants/constant.py @@ -0,0 +1,65 @@ +class Defaults: + room_capacity = 60 + working_days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] + starting_section_fitness = 1000 + max_class_capacity = 250 + initial_no_of_chromosomes = 1000 + total_no_of_generations = 100 + class_strength = 50 + + + +class TeacherPreferences: + def __init__(self, teacher_weekly_workload: dict): + self.teacher_preferences = {} + for teacher, workload in teacher_weekly_workload.items(): + self.teacher_preferences[teacher] = list(range(1, 8)) + + def get_preferences(self): + return self.teacher_preferences + + +class SubjectWeeklyQuota: + def __init__(self, subject_quota): + self.subject_quota = subject_quota + + +class TeacherPreloads: + def __init__(self, teacher_preferences: dict, weekly_workload: dict): + self.teacher_preferences = teacher_preferences + self.weekly_workload = weekly_workload + + +class TeachersDutyDays: + def __init__(self, teacher_duty_days: dict): + self.teacher_duty_days = teacher_duty_days + + +class SpecialSubjects: + def __init__( + self, special_subjects: list, labs: list, specialization_subjects: list + ): + self.special_subjects = special_subjects + self.labs = labs + self.specialization_subjects = specialization_subjects + + +class PenaltyConstants: + PENALTY_TEACHER_DOUBLE_BOOKED = 30 + PENALTY_CLASSROOM_DOUBLE_BOOKED = 20 + PENALTY_OVER_CAPACITY = 25 + PENALTY_UN_PREFERRED_SLOT = 5 + PENALTY_OVERLOAD_TEACHER = 10 + PENALTY_NON_DUTY_DAY = 40 + + +class SectionsConstants: + # Define attribute weights and conditions + ATTRIBUTE_WEIGHTS = { + "good_cgpa": 1, # 2^0 + "hostler": 2, # 2^1 + } + + ATTRIBUTE_CONDITIONS = { + "hostler": lambda student: student.get("is_hosteller", False), + } diff --git a/build/lib/Constants/helper_routines.py b/build/lib/Constants/helper_routines.py new file mode 100644 index 0000000..7b8a685 --- /dev/null +++ b/build/lib/Constants/helper_routines.py @@ -0,0 +1,39 @@ +def update_teacher_availability_matrix(teacher_availability_matrix, best_chromosome): + for teacher_id, schedule_data in best_chromosome.items(): + if teacher_id in teacher_availability_matrix: + teacher_availability_matrix[teacher_id] = schedule_data + return teacher_availability_matrix + + +def update_matrix_for_best( + best_chromosome, teacher_availability_matrix, day_map, time_slot_map +): + for day_name, section_dict in best_chromosome.items(): + if day_name not in day_map: + continue # or raise an error + day_index = day_map[day_name] + + for section_name, schedule_items in section_dict.items(): + for item in schedule_items: + teacher_id = item["teacher_id"] + if teacher_id == "None": + continue + + time_slot_str = item["time_slot"] + if time_slot_str not in time_slot_map: + continue # or raise an error + slot_index = time_slot_map[time_slot_str] + + # Now set availability to False + teacher_availability_matrix[teacher_id][day_index][ + slot_index - 1 + ] = False + + return teacher_availability_matrix + + +def initialize_teacher_availability(teacher_list, num_days=5, num_slots=7): + return { + teacher: [[True] * num_slots for _ in range(num_days)] + for teacher in teacher_list + } diff --git a/build/lib/Constants/is_conflict.py b/build/lib/Constants/is_conflict.py new file mode 100644 index 0000000..dfb98b3 --- /dev/null +++ b/build/lib/Constants/is_conflict.py @@ -0,0 +1,138 @@ +import psycopg2 + +from Samples.samples import SampleChromosome + + +class IsConflict: + def __init__( + self, + dbname="timetable_postgres_db", + user="postgres", + password="13989333", + host="localhost", + port="5432", + ): + self.dbname = dbname + self.user = user + self.password = password + self.host = host + self.port = port + self.conn = None + self.cursor = None + + def connect_to_database(self): + try: + self.conn = psycopg2.connect( + dbname=self.dbname, + user=self.user, + password=self.password, + host=self.host, + port=self.port, + ) + self.cursor = self.conn.cursor() + except Exception as e: + print(f"Error connecting to database: {e}") + raise + + def insert_schedule(self, timetable, chromosome_name): + try: + for day, classes in timetable.items(): + for cls in classes: + self.cursor.execute( + """ + INSERT INTO schedule (chromosome, day, teacher_id, subject_id, classroom_id, time_slot) + VALUES (%s, %s, %s, %s, %s, %s); + """, + ( + chromosome_name, + day, + cls["teacher_id"], + cls["subject_id"], + "N/A", + cls["time_slot"], + ), + ) + self.conn.commit() + except Exception as e: + print(f"Error inserting schedule: {e}") + self.conn.rollback() + + def detect_teacher_conflicts(self): + try: + self.cursor.execute( + """ + SELECT s1.teacher_id, s1.day, s1.time_slot + FROM schedule s1 + JOIN schedule s2 + ON s1.teacher_id = s2.teacher_id + AND s1.time_slot = s2.time_slot + AND s1.day = s2.day + AND ( + s1.chromosome != s2.chromosome -- Conflict across chromosomes + OR s1.chromosome = s2.chromosome -- Conflict within the same chromosome + ) + GROUP BY s1.teacher_id, s1.day, s1.time_slot + HAVING COUNT(s1.teacher_id) > 1; -- More than one class for the same teacher at the same time + """ + ) + return self.cursor.fetchall() + except Exception as e: + print(f"Error detecting teacher conflicts: {e}") + return [] + + def truncate_schedule(self): + try: + self.cursor.execute("TRUNCATE TABLE schedule;") + self.conn.commit() + except Exception as e: + print(f"Error truncating schedule: {e}") + self.conn.rollback() + + def close_connection(self): + try: + if self.cursor: + self.cursor.close() + if self.conn: + self.conn.close() + except Exception as e: + print(f"Error closing connection: {e}") + + def process_schedules(self, timetable1, timetable2): + try: + self.connect_to_database() + + self.insert_schedule(timetable1, "Week 1") + self.insert_schedule(timetable2, "Week 2") + + teacher_conflicts = self.detect_teacher_conflicts() + conflicts = [] + + for conflict in teacher_conflicts: + conflicts.append( + { + "type": "Teacher Conflict", + "teacher": conflict[0], + "day": conflict[1], + "time_slot": conflict[2], + } + ) + + return conflicts + finally: + self.truncate_schedule() + self.close_connection() + + +if __name__ == "__main__": + timetable_processor = IsConflict() + sample_chromosome_1 = SampleChromosome() + sample_chromosome_2 = SampleChromosome() + chromosome1 = sample_chromosome_1.schedule1 + chromosome2 = sample_chromosome_2.schedule2 + + # Process the timetables and check for conflicts + conflicts = timetable_processor.process_schedules(chromosome1, chromosome2) + + # Print the detected conflicts (if any) + print("Conflicts detected:") + print(conflicts) diff --git a/build/lib/Constants/is_conflict_new.py b/build/lib/Constants/is_conflict_new.py new file mode 100644 index 0000000..0433014 --- /dev/null +++ b/build/lib/Constants/is_conflict_new.py @@ -0,0 +1,154 @@ +import psycopg2 + +from Samples.samples import SampleChromosome + + +class IsConflict: + def __init__( + self, + dbname="timetable", + user="postgres", + password="root", + host="localhost", + port="5432", + ): + self.dbname = dbname + self.user = user + self.password = password + self.host = host + self.port = port + self.conn = None + self.cursor = None + + def connect_to_database(self): + try: + self.conn = psycopg2.connect( + dbname=self.dbname, + user=self.user, + password=self.password, + host=self.host, + port=self.port, + ) + self.cursor = self.conn.cursor() + print("Connected to PostgreSQL database!") + except Exception as e: + print(f"Error connecting to the database: {e}") + raise + + def insert_schedule(self, timetable, chromosome_name): + try: + for day, sections in timetable.items(): + for section, classes in sections.items(): + for cls in classes: + print( + f"Inserting data: {chromosome_name}, {day}, {section}, {cls['teacher_id']}, {cls['subject_id']}, {cls['classroom_id']}, {cls['time_slot']}" + ) + self.cursor.execute( + """ + INSERT INTO schedule (chromosome, day, section, teacher_id, subject_id, classroom_id, time_slot) + VALUES (%s, %s, %s, %s, %s, %s, %s); + """, + ( + chromosome_name, + day, + section, + cls["teacher_id"], + cls["subject_id"], + cls["classroom_id"], + cls["time_slot"], + ), + ) + self.conn.commit() + print(f"Schedule data for {chromosome_name} inserted successfully.") + except Exception as e: + self.conn.rollback() + print(f"Error inserting schedule data: {e}") + + def detect_teacher_conflicts(self): + self.cursor.execute( + """ + SELECT s1.teacher_id, s1.day, s1.time_slot, COUNT(*) + FROM schedule s1 + JOIN schedule s2 + ON s1.teacher_id = s2.teacher_id + AND s1.time_slot = s2.time_slot + AND s1.day = s2.day + AND s1.chromosome != s2.chromosome + GROUP BY s1.teacher_id, s1.day, s1.time_slot + HAVING COUNT(*) > 1; + """ + ) + return self.cursor.fetchall() + + def detect_classroom_conflicts(self): + self.cursor.execute( + """ + SELECT s1.classroom_id, s1.day, s1.time_slot, COUNT(*) + FROM schedule s1 + JOIN schedule s2 + ON s1.classroom_id = s2.classroom_id + AND s1.time_slot = s2.time_slot + AND s1.day = s2.day + AND s1.chromosome != s2.chromosome + GROUP BY s1.classroom_id, s1.day, s1.time_slot + HAVING COUNT(*) > 1; + """ + ) + return self.cursor.fetchall() + + def truncate_schedule(self): + self.cursor.execute("TRUNCATE TABLE schedule;") + self.conn.commit() + print("Schedule table truncated.") + + def close_connection(self): + if self.cursor: + self.cursor.close() + if self.conn: + self.conn.close() + print("\nDatabase connection closed.") + + def process_schedules(self, timetable1, timetable2): + try: + self.connect_to_database() + + # Insert Chromosome data for both timetables + self.insert_schedule(timetable1, "Week 1") + self.insert_schedule(timetable2, "Week 2") + + # Check for Teacher Conflicts + teacher_conflicts = self.detect_teacher_conflicts() + + # Check for Classroom Conflicts + classroom_conflicts = self.detect_classroom_conflicts() + + # Return both conflicts as a dictionary + return { + "teacher_conflicts": teacher_conflicts, + "classroom_conflicts": classroom_conflicts, + } + + finally: + # Cleanup and close database connection + self.truncate_schedule() + self.close_connection() + + +if __name__ == "__main__": + timetable_processor = IsConflict() + sample_chromosome_1 = SampleChromosome() + sample_chromosome_2 = SampleChromosome() + chromosome1 = sample_chromosome_1.schedule1 + chromosome2 = sample_chromosome_2.schedule2 + conflicts = timetable_processor.process_schedules(chromosome1, chromosome2) + print("\nTeacher Conflicts:") + for conflict in conflicts["teacher_conflicts"]: + print( + f"Teacher ID: {conflict[0]}, Day: {conflict[1]}, Time Slot: {conflict[2]}, Conflict Count: {conflict[3]}" + ) + + print("\nClassroom Conflicts:") + for conflict in conflicts["classroom_conflicts"]: + print( + f"Classroom ID: {conflict[0]}, Day: {conflict[1]}, Time Slot: {conflict[2]}, Conflict Count: {conflict[3]}" + ) diff --git a/build/lib/Constants/section_allocation.py b/build/lib/Constants/section_allocation.py new file mode 100644 index 0000000..687928d --- /dev/null +++ b/build/lib/Constants/section_allocation.py @@ -0,0 +1,78 @@ +import random +from collections import defaultdict +from typing import Dict, List +from Constants.constant import SectionsConstants + + +class StudentScorer: + def __init__(self, attribute_weights: Dict[str, int] = None): + """ + Initialize the scorer with attribute weights. + Defaults to SectionsConstants.ATTRIBUTE_WEIGHTS if not provided. + """ + self.attribute_weights = attribute_weights or SectionsConstants.ATTRIBUTE_WEIGHTS + + def calculate_dynamic_cgpa_threshold(self, students: List[Dict], top_percentage: int = 30) -> float: + """ + Calculate the CGPA threshold for the top X% of students. + """ + valid_cgpas = [student.get("cgpa", 0) for student in students if isinstance(student.get("cgpa"), (int, float))] + if not valid_cgpas: + return 0 # Default if no valid CGPAs + sorted_cgpas = sorted(valid_cgpas, reverse=True) + threshold_index = max(1, int(len(sorted_cgpas) * top_percentage / 100)) - 1 + return sorted_cgpas[threshold_index] + + def assign_dynamic_conditions(self, cgpa_threshold: float): + """ + Assign a dynamic condition for CGPA based on the threshold. + """ + SectionsConstants.ATTRIBUTE_CONDITIONS["good_cgpa"] = lambda student: student.get("cgpa", 0) >= cgpa_threshold + + def calculate_student_score(self, student: Dict) -> int: + """ + Calculate the score for a student based on attribute weights. + """ + total_score = 0 + for attribute, weight in self.attribute_weights.items(): + condition_func = SectionsConstants.ATTRIBUTE_CONDITIONS.get(attribute, lambda x: False) + if condition_func(student): + total_score += weight + return total_score + + def assign_scores_to_students(self, students): + # Step 1: Calculate CGPA threshold + cgpa_threshold = self.calculate_dynamic_cgpa_threshold(students) + self.assign_dynamic_conditions(cgpa_threshold) + + + # Step 2: Assign scores + for student in students: + student["score"] = self.calculate_student_score(student) + + + return students + + def divide_students_into_sections(self, students: List[Dict], class_strength: int = 50) -> List[List[Dict]]: + """ + Divide students into sections based on their scores and class strength. + """ + grouped_by_score = defaultdict(list) + for student in students: + grouped_by_score[student["score"]].append(student) + grouped_by_score[student["score"]].append(student) + + sections = [] + current_section = [] + + for score_group in grouped_by_score.values(): + for student in score_group: + current_section.append(student) + if len(current_section) == class_strength: + sections.append(current_section) + current_section = [] + + if current_section: + sections.append(current_section) + + return sections diff --git a/build/lib/Constants/teacher_tt_csv.py b/build/lib/Constants/teacher_tt_csv.py new file mode 100644 index 0000000..9f5418f --- /dev/null +++ b/build/lib/Constants/teacher_tt_csv.py @@ -0,0 +1,81 @@ +import os +import json +import csv +from Samples.samples import (WorkingDays, SampleChromosome) +from Constants.teachers_tt import TeacherTimetable + +# Predefined time slots mapping +time_slots = { + 1: "08:00 - 09:00", + 2: "09:00 - 10:00", + 3: "11:00 - 12:00", + 4: "12:00 - 13:00", + 5: "16:50 - 17:50", +} + +# Reverse lookup to get slot number from time string +time_slot_order = {v: k for k, v in time_slots.items()} + +# List of days in the correct order +days_of_week_order = WorkingDays.days + +def extract_time_slots(timetable): + """Extract and sort time slots without inserting breaks or lunch.""" + unique_slots = set() + + # Collect all unique time slots from timetable + for day_data in timetable.values(): + for slots in day_data.values(): + for entry in slots: + unique_slots.add(entry["time_slot"]) + + # Sort slots based on predefined order + sorted_slots = sorted(unique_slots, key=lambda slot: time_slot_order.get(slot, float('inf'))) + + # Only include class slots + final_slots = [("CLASS", slot) for slot in sorted_slots] + + return final_slots + +def teacher_json_to_csv(teacher_timetable, output_folder): + """Convert teacher timetable dictionary to CSV.""" + os.makedirs(output_folder, exist_ok=True) + + for teacher_id, schedule in teacher_timetable.items(): + final_slots = extract_time_slots(schedule) + csv_file = os.path.join(output_folder, f"{teacher_id}.csv") + header = ["DAY"] + [slot[1] for slot in final_slots] + rows = [] + + # Sort days correctly + sorted_days = sorted(schedule.items(), key=lambda x: days_of_week_order.index(x[0])) + + for day, day_data in sorted_days: + row = [day] + ["" for _ in final_slots] # Empty slots initially + + for section, classes in day_data.items(): + for entry in classes: + for i, slot in enumerate(final_slots): + if entry["time_slot"] == slot[1]: + row[i + 1] = f"{entry['subject_id']} ({section}, {entry['classroom_id']})" + + rows.append(row) + + with open(csv_file, mode="w", newline="", encoding="utf-8") as file: + writer = csv.writer(file) + writer.writerow(header) + writer.writerows(rows) + +if __name__ == "__main__": + # Generate teacher-wise timetable + teacher_timetable = TeacherTimetable() + w = { + "Week 2": SampleChromosome.schedule2, + "Week 1": SampleChromosome.schedule1 + } + teacher_tt = teacher_timetable.generate_teacher_timetable(w) + + # Convert generated timetable to CSVs + output_folder = "tt_csvs" # Folder to save CSVs + teacher_json_to_csv(teacher_tt, output_folder) +w \ No newline at end of file diff --git a/build/lib/Constants/teachers_tt.py b/build/lib/Constants/teachers_tt.py new file mode 100644 index 0000000..68b7377 --- /dev/null +++ b/build/lib/Constants/teachers_tt.py @@ -0,0 +1,93 @@ +import json + +from Samples.samples import SampleChromosome, SubjectTeacherMap, WorkingDays + + +class TeacherTimetable: + def __init__(self): + # Create timetable for each teacher + self.teacher_timetable = { + teacher: {day: [] for day in WorkingDays.days} + for teachers in SubjectTeacherMap.subject_teacher_map.values() + for teacher in teachers + } + self.teacher_assignments = { + teacher: {} + for teachers in SubjectTeacherMap.subject_teacher_map.values() + for teacher in teachers + } + + def generate_teacher_timetable(self, chromosome): + """ + Populate the teacher's timetable based on the provided chromosome schedule. + Now includes the course (Week 1, Week 2, etc.) information. + """ + for week, days in chromosome.items(): + for day, sections in days.items(): + for section, classes in sections.items(): + for entry in classes: + teacher_id = entry["teacher_id"] + subject_id = entry["subject_id"] + time_slot = entry["time_slot"] + classroom_id = entry["classroom_id"] + + # Conflict check: Ensure the teacher is not already assigned at this time slot + if ( + teacher_id in self.teacher_assignments + and time_slot in self.teacher_assignments[teacher_id] + ): + continue # Skip this assignment if there's a conflict + + # Add the class to the teacher's timetable + self.teacher_timetable[teacher_id][day].append( + { + "course": week, # Include course information (Week 1, Week 2) + "section": section, + "subject_id": subject_id, + "time_slot": time_slot, + "classroom_id": classroom_id, + } + ) + + # Update the teacher's assignments to reflect the added class + if teacher_id not in self.teacher_assignments: + self.teacher_assignments[teacher_id] = {} + self.teacher_assignments[teacher_id][time_slot] = { + "course": week, + "section": section, + "subject_id": subject_id, + "classroom_id": classroom_id, + "day": day, + } + + return self.teacher_timetable + + def save_timetable_to_json(self, file_path="Constants/teacher_timetable.json"): + """ + Create a dictionary and save the teacher's timetable to a JSON file. + """ + try: + # Create a dictionary explicitly from the teacher timetable + timetable_dict = { + teacher: {day: classes for day, classes in days.items()} + for teacher, days in self.teacher_timetable.items() + } + + # Save the dictionary to a JSON file + with open(file_path, "w") as json_file: + json.dump(timetable_dict, json_file, indent=4) + + print(f"Timetable successfully saved to '{file_path}'.") + except Exception as e: + print(f"Error saving timetable to '{file_path}': {e}") + + +if __name__ == "__main__": + teacher_timetable = TeacherTimetable() + # Generate timetable from the sample chromosome (Week 1 and Week 2) + w = {"Week 2": SampleChromosome.schedule2, "Week 1": SampleChromosome.schedule1} + + teacher_tt = teacher_timetable.generate_teacher_timetable(w) + print(teacher_tt) + # Save timetable to a JSON file + teacher_timetable.save_timetable_to_json() diff --git a/build/lib/Constants/time_intervals.py b/build/lib/Constants/time_intervals.py new file mode 100644 index 0000000..39444a3 --- /dev/null +++ b/build/lib/Constants/time_intervals.py @@ -0,0 +1,127 @@ +from datetime import datetime, timedelta + + +class TimeIntervalConstant: + time_slots = {} + time_mapping = {} + + @staticmethod + def generate_dynamic_schedule( + start_time, + period_duration=55, + total_periods=7, + break_after_periods=None, + lunch_after_period=4, + break_duration=10, + lunch_duration=30, + ): + if break_after_periods is None: + break_after_periods = set() + TimeIntervalConstant.time_slots.clear() + TimeIntervalConstant.time_mapping.clear() + + start_time = datetime.strptime(start_time, "%H:%M") + + current_time = start_time + period_counter = 1 + + while period_counter <= total_periods: + end_time = current_time + timedelta(minutes=period_duration) + time_slot = ( + f"{current_time.strftime('%H:%M')} - {end_time.strftime('%H:%M')}" + ) + TimeIntervalConstant.time_slots[period_counter] = time_slot + TimeIntervalConstant.time_mapping[time_slot] = period_counter + current_time = end_time + if period_counter in break_after_periods: + current_time += timedelta(minutes=break_duration) + if period_counter == lunch_after_period: + current_time += timedelta(minutes=lunch_duration) + + period_counter += 1 + + @staticmethod + def get_slot(slot_number: int) -> str: + """Retrieve time slot based on slot number. + + Args: + slot_number (int): The slot number corresponding to a time interval. + + Returns: + str: The time interval for the given slot number. + + Raises: + ValueError: If the slot number is out of the valid range. + """ + + if slot_number in TimeIntervalConstant.time_slots: + return TimeIntervalConstant.time_slots[slot_number] + + else: + max_slot = len(TimeIntervalConstant.time_slots) + raise ValueError( + f"Invalid slot number. Please provide a slot number between 1 and {max_slot + 1}." + ) + + @classmethod + def get_slot_number(cls, start_time: str, end_time: str): + """Get the slot number for a given start and end time. + + Args: + start_time (str): The start time in "HH:MM" format. + end_time (str): The end time in "HH:MM" format. + + Returns: + int or str: The slot number if found; otherwise, returns an error message. + """ + + try: + # Format start and end times to ensure they only include hours and minutes + start_time = str(datetime.strptime(start_time, "%H:%M").time()).strip(" ") + end_time = str(datetime.strptime(end_time, "%H:%M").time()).strip(" ") + + for slot, interval in cls.time_slots.items(): + # Making string in this format: "3:30 - 4:25". + slot_start_time = datetime.strptime( + interval.split(" - ")[0], "%H:%M" + ).time() + slot_end_time = datetime.strptime( + interval.split(" - ")[1], "%H:%M" + ).time() + + if start_time == slot_start_time and end_time == slot_end_time: + return slot + raise LookupError("No Time slot for this time interval found!") + + except ValueError: + raise ValueError("Invalid time format!") + + @staticmethod + def get_all_slot_numbers(): + """Retrieve all slot numbers.""" + return list(TimeIntervalConstant.time_slots.keys()) + + @staticmethod + def get_all_time_slots(): + """Retrieve all time intervals.""" + return list(TimeIntervalConstant.time_slots.values()) + + +TimeIntervalConstant.generate_dynamic_schedule( + "8:00", # Start time (positional argument) + 60, # Period duration (positional argument) + 7, # Total periods (positional argument) + {2, 6}, # Break after periods (positional argument) + 4, # Lunch after period (positional argument) + 60, # Break duration (positional argument) + 50, # Lunch duration (positional argument) +) + +slot_numbers = TimeIntervalConstant.get_all_slot_numbers() + + +time_slots = TimeIntervalConstant.get_all_time_slots() + + +# print("Slot Numbers:", slot_numbers) +# print("Time Slots:", time_slots) diff --git a/build/lib/Constants/timetable_csv.py b/build/lib/Constants/timetable_csv.py new file mode 100644 index 0000000..1b59b94 --- /dev/null +++ b/build/lib/Constants/timetable_csv.py @@ -0,0 +1,124 @@ +import os +import json +import csv +from datetime import datetime + +# Predefined time slots mapping +time_slots = { + 1: "8:00 - 9:00", + 2: "9:00 - 10:00", + 3: "11:10 - 12:05", + 4: "12:05 - 1:00", + 5: "1:20 - 2:15", + 6: "2:15 - 3:10", + 7: "3:30 - 4:25", +} + +# Reverse lookup to get slot number from time string +time_slot_order = {v: k for k, v in time_slots.items()} + +# List of days in the order we want them to appear +days_of_week_order = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] + +def parse_time(time_slot): + """Extract start and end times from a time slot string.""" + start_time_str, end_time_str = time_slot.split(" - ") + start_time = datetime.strptime(start_time_str.strip(), "%I:%M").time() + end_time = datetime.strptime(end_time_str.strip(), "%I:%M").time() + return start_time, end_time + +def extract_time_slots(timetable): + """Extract, sort time slots, and insert BREAK/LUNCH where needed.""" + unique_slots = set() + + # Collect all unique time slots from timetable + for day_data in timetable.values(): + for slots in day_data.values(): + for entry in slots: + unique_slots.add(entry["time_slot"]) + + # Sort slots based on predefined order + sorted_slots = sorted(unique_slots, key=lambda slot: time_slot_order[slot]) + + # Insert BREAK and LUNCH dynamically based on gaps + final_slots = [] + last_end_time = None + break_lunch_counter = 0 + + for slot in sorted_slots: + start_time, end_time = parse_time(slot) + + if last_end_time: + gap = (datetime.combine(datetime.today(), start_time) - datetime.combine(datetime.today(), last_end_time)).seconds / 60 + + if gap > 5: # If there's a gap greater than 5 minutes + break_time = f"{last_end_time.strftime('%I:%M')} - {start_time.strftime('%I:%M')}" + if break_lunch_counter == 0: + final_slots.append(("BREAK", break_time)) + elif break_lunch_counter == 1: + final_slots.append(("LUNCH", break_time)) + else: + final_slots.append(("BREAK", break_time)) + + break_lunch_counter += 1 # Increment counter + + final_slots.append(("CLASS", slot)) + last_end_time = end_time # Update last_end_time + + return final_slots + +def json_to_csv(input_folder, json_filename, output_folder): + """Convert JSON timetable to CSV with breaks and lunches inserted.""" + json_file = os.path.join(input_folder, json_filename) + + with open(json_file, "r", encoding="utf-8") as file: + timetable = json.load(file) + + sections = set() + final_slots = extract_time_slots(timetable) + + os.makedirs(output_folder, exist_ok=True) + + # Identify all sections + for day_data in timetable.values(): + for section in day_data.keys(): + sections.add(section) + + # Sort days in the correct order + sorted_days = sorted(timetable.items(), key=lambda x: days_of_week_order.index(x[0])) + + for section in sections: + csv_file = os.path.join(output_folder, f"{section}.csv") + header = ["DAY"] + [slot[1] for slot in final_slots] # Extract only time for header + rows = [] + + # Iterate over sorted days in the correct order + for day, day_data in sorted_days: # Sorted days based on days_of_week_order + # Initialize row with blank spaces + row = [day] + [""] * len(final_slots) # Initially empty for each time slot + + if section in day_data: + for entry in day_data[section]: + for i, slot in enumerate(final_slots): + # Compare and add the class schedule, BREAK, or LUNCH + if entry["time_slot"] == slot[1] and slot[0] == "CLASS": + row[i + 1] = f"{entry['subject_id']} ({entry['teacher_id']}, {entry['classroom_id']})" + elif slot[0] == "BREAK" or slot[0] == "LUNCH": + # Add BREAK and LUNCH to appropriate slots + row[i + 1] = slot[0] + + rows.append(row) + + # Write the row into CSV + with open(csv_file, mode="w", newline="", encoding="utf-8") as file: + writer = csv.writer(file) + writer.writerow(header) + writer.writerows(rows) + + print(f"CSV file '{csv_file}' has been created successfully!") + +if __name__ == "_main_": + input_folder = "." # Your input folder where the JSON file is located + json_filename = "new.json" # Replace with your actual JSON filename + output_folder = "output_csvs" # Output folder where CSV files will be saved + json_to_csv(input_folder, json_filename, output_folder) \ No newline at end of file diff --git a/build/lib/GA/__init__.py b/build/lib/GA/__init__.py new file mode 100644 index 0000000..fd7f346 --- /dev/null +++ b/build/lib/GA/__init__.py @@ -0,0 +1,258 @@ +import copy +from dataclasses import dataclass + +from Constants.helper_routines import ( + initialize_teacher_availability, + update_matrix_for_best, + update_teacher_availability_matrix, +) +from GA.chromosome import TimeTableGeneration +from GA.fitness import TimetableFitnessEvaluator +from GA.mutation import TimeTableCrossOver, TimeTableMutation +from GA.selection import TimeTableSelection + + +@dataclass +class TimetableConfig: + teacher_subject_mapping: dict + total_sections: dict + total_classrooms: dict + total_labs: dict + teacher_preferences: dict + teacher_weekly_workload: dict + special_subjects: dict + labs: dict + subject_quota_limits: dict + teacher_duty_days: dict + teacher_availability_matrix: dict + lab_availability_matrix: dict + total_generations: int + time_slots: dict + day_map: dict + time_slot_map: dict + prev_selected: dict = None + prev_mutated: list = None + fixed_teacher_assignment: dict = None + + +class TimetableEngine: + def __init__(self, config: TimetableConfig): + self.config = config + self.teacher_availability = copy.deepcopy(config.teacher_availability_matrix) + self.lab_availability = copy.deepcopy(config.lab_availability_matrix) + + def _update_lab_availability(self, best_timetable): + updated_lab = copy.deepcopy(self.config.lab_availability_matrix) + for weekday, daily_schedule in best_timetable.items(): + day_index = self.config.day_map.get(weekday) + if day_index is None: + continue + for allocations in daily_schedule.values(): + for alloc in allocations: + lab = alloc.get("classroom_id") + ts_index = self.config.time_slot_map.get(alloc.get("time_slot")) + if lab in updated_lab and ts_index is not None: + updated_lab[lab][day_index][ts_index - 1] = False + return updated_lab + + def _generate_timetable(self, teacher_matrix): + tg = TimeTableGeneration( + teacher_subject_mapping=self.config.teacher_subject_mapping, + total_sections=self.config.total_sections, + total_classrooms=self.config.total_classrooms, + total_labs=self.config.total_labs, + teacher_preferences=self.config.teacher_preferences, + teacher_weekly_workload=self.config.teacher_weekly_workload, + special_subjects=self.config.special_subjects, + labs=self.config.labs, + subject_quota_limits=self.config.subject_quota_limits, + teacher_duty_days=self.config.teacher_duty_days, + teacher_availability_matrix=teacher_matrix, + lab_availability_matrix=self.lab_availability, + time_slots=self.config.time_slots, + fixed_teacher_assignment=self.config.fixed_teacher_assignment or {} + ) + timetable, updated_teacher = tg.create_timetable(self.config.total_generations)[:2] + fitness = TimetableFitnessEvaluator( + timetable=timetable, + all_sections=list(tg.sections_manager.keys()), + subject_teacher_mapping=tg.subject_teacher_mapping, + available_classrooms=list(tg.classrooms_manager.keys()), + available_labs=list(tg.lab_capacity_manager.keys()), + classroom_capacity=tg.classrooms_manager, + section_student_strength=tg.sections_manager, + subject_quota_data=tg.subject_quota_limits, + teacher_time_preferences=tg.teacher_availability_preferences, + teacher_daily_workload=tg.weekly_workload, + time_slots=self.config.time_slots, + ).evaluate_timetable_fitness() + + selected = TimeTableSelection().select_chromosomes(fitness[1]) + if self.config.prev_selected: + selected.update(self.config.prev_selected) + + crossover = TimeTableCrossOver() + crossover_chromosomes = [] + selected_keys = list(selected.keys()) + for i in range(0, len(selected_keys), 2): + if i + 1 < len(selected_keys): + c1, c2 = crossover.perform_crossover( + timetable[selected_keys[i]], timetable[selected_keys[i + 1]] + ) + crossover_chromosomes.extend([c1, c2]) + + mutated = [TimeTableMutation().mutate_schedule_for_week(ch) for ch in crossover_chromosomes] + if self.config.prev_mutated: + mutated.extend(self.config.prev_mutated) + + best_chromosome, best_score = None, -1 + for key, score in selected.items(): + score = int(score) + if score > best_score and key in timetable: + best_score = score + best_chromosome = timetable[key] + + if best_chromosome: + updated_teacher = update_teacher_availability_matrix(teacher_matrix, best_chromosome) + return best_chromosome, updated_teacher, selected, mutated + + def run(self): + initial_teacher = copy.deepcopy(self.teacher_availability) + updated_teacher = copy.deepcopy(initial_teacher) + best_chromosome = None + + self.config.prev_selected = None + self.config.prev_mutated = None + + for _ in range(self.config.total_generations): + teacher_copy = copy.deepcopy(initial_teacher) + best, updated_teacher, selected, mutated = self._generate_timetable(teacher_copy) + best_chromosome = best + + self.config.prev_selected = selected + self.config.prev_mutated = mutated + + updated_teacher = update_matrix_for_best( + best_chromosome, + updated_teacher, + self.config.day_map, + self.config.time_slot_map, + ) + + updated_lab = self._update_lab_availability(best_chromosome) + return best_chromosome, updated_teacher, updated_lab + + +def run_timetable_generation( + teacher_subject_mapping: dict, + total_sections: dict, + total_classrooms: dict, + total_labs: dict, + teacher_preferences: dict, + teacher_weekly_workload: dict, + special_subjects: dict, + labs: dict, + subject_quota_limits: dict, + teacher_duty_days: dict, + teacher_availability_matrix: dict, + lab_availability_matrix: dict, + total_generations: int, + time_slots: dict, + day_map: dict, + time_slot_map: dict, + fixed_teacher_assignment: dict = None +): + config = TimetableConfig( + teacher_subject_mapping=teacher_subject_mapping, + total_sections=total_sections, + total_classrooms=total_classrooms, + total_labs=total_labs, + teacher_preferences=teacher_preferences, + teacher_weekly_workload=teacher_weekly_workload, + special_subjects=special_subjects, + labs=labs, + subject_quota_limits=subject_quota_limits, + teacher_duty_days=teacher_duty_days, + teacher_availability_matrix=teacher_availability_matrix, + lab_availability_matrix=lab_availability_matrix, + total_generations=total_generations, + time_slots=time_slots, + day_map=day_map, + time_slot_map=time_slot_map, + fixed_teacher_assignment=fixed_teacher_assignment or {} + ) + engine = TimetableEngine(config) + return engine.run() + + +if __name__ == "__main__": + from Constants.constant import Defaults + from Samples.samples import ( + SpecialSubjects, + SubjectTeacherMap, + SubjectWeeklyQuota, + TeacherWorkload, + ) + + lab_matrix = { + "L1": [[True] * 7 for _ in range(5)], + "L2": [[True] * 7 for _ in range(5)], + "L3": [[True] * 7 for _ in range(5)], + "L4": [[True] * 7 for _ in range(5)], + "L5": [[True] * 7 for _ in range(5)], + "L6": [[True] * 7 for _ in range(5)], + } + + teacher_availability = initialize_teacher_availability( + TeacherWorkload.Weekly_workLoad.keys(), 5, 7 + ) + + best_tt, final_teacher, final_lab = run_timetable_generation( + teacher_subject_mapping=SubjectTeacherMap.subject_teacher_map, + total_sections={"A": 70, "B": 100, "C": 75, "D": 100}, + total_classrooms={"R1": 200, "R2": 230, "R3": 240, "R4": 250, "R5": 250}, + total_labs={"L1": 70, "L2": 50, "L3": 70, "L4": 50, "L5": 70, "L6": 50}, + teacher_preferences=TeacherWorkload.teacher_preferences, + teacher_weekly_workload=TeacherWorkload.Weekly_workLoad, + special_subjects=SpecialSubjects.special_subjects, + labs=SpecialSubjects.Labs, + subject_quota_limits=SubjectWeeklyQuota.subject_quota, + teacher_duty_days=TeacherWorkload.teacher_duty_days, + teacher_availability_matrix=teacher_availability, + lab_availability_matrix=lab_matrix, + total_generations=Defaults.total_no_of_generations, + time_slots={ + 1: "9:00 - 9:55", + 2: "9:55 - 10:50", + 3: "11:10 - 12:05", + 4: "12:05 - 1:00", + 5: "1:20 - 2:15", + 6: "2:15 - 3:10", + 7: "3:30 - 4:25", + }, + day_map={ + "Monday": 0, + "Tuesday": 1, + "Wednesday": 2, + "Thursday": 3, + "Friday": 4, + "Saturday": 5, + "Sunday": 6, + }, + time_slot_map={ + "9:00 - 9:55": 1, + "9:55 - 10:50": 2, + "11:10 - 12:05": 3, + "12:05 - 1:00": 4, + "1:20 - 2:15": 5, + "2:15 - 3:10": 6, + "3:30 - 4:25": 7, + }, + fixed_teacher_assignment={ + "A": {"TCS-531": "AB01", "TMA-502": "HP18"}, # section: {"subject": teacher} + "B": {"TCS-503": "BJ10"} + } + ) + + from icecream import ic + ic(best_tt, final_teacher, final_lab) diff --git a/build/lib/GA/chromosome.py b/build/lib/GA/chromosome.py new file mode 100644 index 0000000..69c7daf --- /dev/null +++ b/build/lib/GA/chromosome.py @@ -0,0 +1,351 @@ +import copy +import random +from math import ceil + +from Constants.constant import Defaults + + +class TimeTableGeneration: + def __init__( + self, + teacher_subject_mapping: dict, + total_sections: dict, + total_classrooms: dict, + total_labs: dict, + teacher_preferences: dict, + teacher_weekly_workload: dict, + special_subjects: dict, + labs: dict, + subject_quota_limits: dict, + teacher_duty_days: dict, + teacher_availability_matrix: dict, + lab_availability_matrix: dict, + time_slots: dict, + fixed_teacher_assignment: dict = None, + ): + self.sections_manager = total_sections + self.classrooms_manager = total_classrooms + self.lab_capacity_manager = total_labs + self.weekdays = Defaults.working_days + self.subject_teacher_mapping = teacher_subject_mapping + self.subject_quota_limits = subject_quota_limits + self.lab_subject_list = labs + self.special_subject_list = special_subjects + self.teacher_availability_preferences = teacher_preferences + self.available_time_slots = time_slots + self.teacher_duty_days = teacher_duty_days + self.weekly_workload = teacher_weekly_workload + self.teacher_availability_matrix = teacher_availability_matrix + self.initial_lab_availability_matrix = copy.deepcopy(lab_availability_matrix) + self.lab_availability_matrix = copy.deepcopy(self.initial_lab_availability_matrix) + self.fixed_teacher_assignment = fixed_teacher_assignment or {} + self._map_sections_to_classrooms() + + def _map_sections_to_classrooms(self) -> dict: + sorted_classrooms = sorted( + self.classrooms_manager.items(), + key = lambda item: item[1], + reverse=True + ) + sorted_sections = sorted( + self.sections_manager.items(), + key = lambda item: item[1], + reverse = True + ) + section_classroom_map = {} + + for section, student_count in sorted_sections: + for index, (classroom, capacity) in enumerate(sorted_classrooms): + if capacity >= student_count: + section_classroom_map[section] = classroom + sorted_classrooms.pop(index) + break + + self.section_to_classroom_map = section_classroom_map + return section_classroom_map + + + def _initialize_teacher_workload_tracker(self) -> dict: + return { + teacher: 0 + for teacher in self.weekly_workload + } + + + def _get_available_subjects(self, section: str, subject_usage_tracker: dict) -> list: + return [ + subject + for subject in self.subject_teacher_mapping + if subject_usage_tracker[section][subject] < self.subject_quota_limits.get(subject, 0) + ] + + + def _assign_subject_and_teacher( + self, + section: str, + slot_index: int, + subjects_scheduled_today: set, + assigned_classroom: str, + subject_usage_tracker: dict, + teacher_workload_tracker: dict, + teacher_availability_matrix: dict, + day_index: int, + ) -> tuple: + available_subjects = self._get_available_subjects(section, subject_usage_tracker) + random.shuffle(available_subjects) + assigned_teacher = None + selected_subject = None + assigned_room = assigned_classroom + + for subject in available_subjects: + if (subject in self.lab_subject_list or subject == "Placement_Class") and slot_index not in [1, 3, 5]: + continue + if subject not in subjects_scheduled_today: + # First, check for a fixed teacher assignment for (section, subject) + fixed_teacher = self.fixed_teacher_assignment.get(section, {}).get(subject) + if fixed_teacher: + if ( + fixed_teacher in teacher_availability_matrix and + len(teacher_availability_matrix[fixed_teacher]) > day_index and + len(teacher_availability_matrix[fixed_teacher][day_index]) > (slot_index - 1) and + teacher_availability_matrix[fixed_teacher][day_index][slot_index - 1] + ): + assigned_teacher = fixed_teacher + teacher_workload_tracker[fixed_teacher] += 1 + selected_subject = subject + subjects_scheduled_today.add(subject) + break + + # Otherwise, assign normally from the available pool + teachers = self.subject_teacher_mapping[subject] + preferred_teachers = [ + t for t in teachers if self.teacher_availability_preferences.get(t, []) + ] + for teacher in sorted(preferred_teachers, key=lambda t: teacher_workload_tracker[t]): + if ( + teacher in teacher_availability_matrix and + len(teacher_availability_matrix[teacher]) > day_index and + len(teacher_availability_matrix[teacher][day_index]) > (slot_index - 1) and + teacher_availability_matrix[teacher][day_index][slot_index - 1] + ): + assigned_teacher = teacher + teacher_workload_tracker[teacher] += 1 + selected_subject = subject + subjects_scheduled_today.add(subject) + break + + if assigned_teacher: + break + + if not assigned_teacher: + selected_subject = "Library" + assigned_teacher = "None" + return assigned_teacher, selected_subject, assigned_room + + + def _allocate_lab( + self, + teacher: str, + subject: str, + day_index: int, + slot_index: int, + section_strength: int, + ) -> tuple: + group1_size = ceil(section_strength / 2) + group2_size = section_strength - group1_size + labs_list = list(self.lab_availability_matrix.keys()) + + for i in range(len(labs_list)): + lab1 = labs_list[i] + if ( + self.lab_availability_matrix[lab1][day_index][slot_index - 1] + and self.lab_availability_matrix[lab1][day_index][slot_index] + and self.lab_capacity_manager.get(lab1, 0) >= group1_size + ): + for j in range(i + 1, len(labs_list)): + lab2 = labs_list[j] + if ( + self.lab_availability_matrix[lab2][day_index][slot_index - 1] + and self.lab_availability_matrix[lab2][day_index][slot_index] + and self.lab_capacity_manager.get(lab2, 0) >= group2_size + ): + self.lab_availability_matrix[lab1][day_index][ + slot_index - 1 + ] = False + self.lab_availability_matrix[lab1][day_index][ + slot_index + ] = False + self.lab_availability_matrix[lab2][day_index][ + slot_index - 1 + ] = False + self.lab_availability_matrix[lab2][day_index][ + slot_index + ] = False + entries = [ + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": lab1, + "time_slot": self.available_time_slots[slot_index], + "group": 1, + }, + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": lab1, + "time_slot": self.available_time_slots[slot_index + 1], + "group": 1, + }, + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": lab2, + "time_slot": self.available_time_slots[slot_index], + "group": 2, + }, + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": lab2, + "time_slot": self.available_time_slots[slot_index + 1], + "group": 2, + }, + ] + return entries, slot_index + 2 + + merged_entry = { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": "merged_lab", + "time_slot": self.available_time_slots[slot_index], + "group": "merged", + "flagged": False, + } + return [merged_entry], slot_index + 1 + + + def _generate_section_schedule( + self, + section: str, + half_day_sections: list, + subject_usage_tracker: dict, + teacher_workload_tracker: dict, + teacher_availability_matrix: dict, + day_index: int, + section_strength: int, + ) -> tuple: + schedule = [] + subjects_scheduled = set() + assigned_classroom = self.section_to_classroom_map[section] + total_slots = 4 if section in half_day_sections else 7 + slot_index = 1 + + while slot_index <= total_slots: + teacher, subject, room = self._assign_subject_and_teacher( + section, + slot_index, + subjects_scheduled, + assigned_classroom, + subject_usage_tracker, + teacher_workload_tracker, + teacher_availability_matrix, + day_index, + ) + time_slot = self.available_time_slots[slot_index] + if subject in self.lab_subject_list or subject == "Placement_Class": + if slot_index <= total_slots - 1: + lab_entries, slot_index = self._allocate_lab( + teacher, subject, day_index, slot_index, section_strength + ) + schedule.extend(lab_entries) + subject_usage_tracker[section][subject] += len(lab_entries) + else: + schedule.append( + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": assigned_classroom, + "time_slot": time_slot, + "group": "fallback", + } + ) + subject_usage_tracker[section][subject] += 1 + slot_index += 1 + else: + schedule.append( + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": room, + "time_slot": time_slot, + "group": "all", + } + ) + if subject != "Library": + subject_usage_tracker[section][subject] += 1 + slot_index += 1 + + return schedule, teacher_availability_matrix + + + def generate_daily_schedule( + self, + sections: list, + half_day_sections: list, + subject_usage_tracker: dict, + day_index: int, + ) -> tuple: + daily_schedule = {} + teacher_workload = self._initialize_teacher_workload_tracker() + for section in sections: + section_strength = self.sections_manager[section] + ( + schedule, + self.teacher_availability_matrix, + ) = self._generate_section_schedule( + section, + half_day_sections, + subject_usage_tracker, + teacher_workload, + self.teacher_availability_matrix, + day_index, + section_strength, + ) + daily_schedule[section] = schedule + return daily_schedule, subject_usage_tracker, self.teacher_availability_matrix + + + def _generate_weekly_schedule(self) -> tuple: + weekly_schedule = {} + subject_usage = { + section: {subject: 0 for subject in self.subject_teacher_mapping.keys()} + for section in self.sections_manager.keys() + } + sections = list(self.sections_manager.keys()) + for day_index, weekday in enumerate(self.weekdays): + random.shuffle(sections) + half_day = sections[: len(sections) // 2] + ( + daily_sched, + subject_usage, + self.teacher_availability_matrix, + ) = self.generate_daily_schedule( + sections, half_day, subject_usage, day_index + ) + weekly_schedule[weekday] = daily_sched + return weekly_schedule, subject_usage, self.teacher_availability_matrix + + + def create_timetable(self, num_weeks: int) -> tuple: + timetable = {} + for week in range(1, num_weeks + 1): + self.lab_availability_matrix = copy.deepcopy( + self.initial_lab_availability_matrix + ) + ( + weekly_schedule, + _, + self.teacher_availability_matrix, + ) = self._generate_weekly_schedule() + timetable[f"Week {week}"] = weekly_schedule + return timetable, self.teacher_availability_matrix, self.lab_availability_matrix diff --git a/build/lib/GA/fitness.py b/build/lib/GA/fitness.py new file mode 100644 index 0000000..d88237e --- /dev/null +++ b/build/lib/GA/fitness.py @@ -0,0 +1,191 @@ +import json + +from Constants.constant import Defaults, PenaltyConstants +from GA.chromosome import TimeTableGeneration +from Samples.samples import ( + SpecialSubjects, + SubjectTeacherMap, + SubjectWeeklyQuota, + TeacherWorkload, +) + + +class TimetableFitnessEvaluator: + def __init__( + self, + timetable, + all_sections, + subject_teacher_mapping, + available_classrooms, + available_labs, + classroom_capacity, + section_student_strength, + subject_quota_data, + teacher_time_preferences, + teacher_daily_workload, + time_slots, + ): + self.timetable = timetable + self.available_days = Defaults.working_days + self.all_sections = all_sections + self.subject_teacher_mapping = subject_teacher_mapping + self.available_classrooms = available_classrooms + self.available_labs = available_labs + self.classroom_capacity = classroom_capacity + self.section_student_strength = section_student_strength + self.subject_quota_data = subject_quota_data + self.teacher_time_preferences = teacher_time_preferences + self.teacher_daily_workload = teacher_daily_workload + self.time_slots = time_slots + + def evaluate_timetable_fitness(self): + daily_section_fitness_scores = {} + weekly_fitness_scores = {} + teacher_workload_tracking = {} + + for week, week_schedule in self.timetable.items(): + weekly_fitness = 0 + daily_section_fitness_scores[week] = {} + teacher_time_slot_tracking = {} + for day, day_schedule in week_schedule.items(): + daily_section_fitness_scores[week][day] = {} + day_fitness = 0 + for section, section_schedule in day_schedule.items(): + section_fitness = Defaults.starting_section_fitness + + classroom_time_slot_tracking = {} + + for schedule_item in section_schedule: + assigned_teacher = schedule_item["teacher_id"] + assigned_classroom = schedule_item["classroom_id"] + assigned_time_slot = self.time_slots.get( + schedule_item["time_slot"] + ) + section_strength = self.section_student_strength.get(section, 0) + + # Penalty 1: Double booking a teacher in the same time slot + if ( + assigned_teacher, + assigned_time_slot, + ) in teacher_time_slot_tracking.keys(): + section_fitness -= ( + PenaltyConstants.PENALTY_TEACHER_DOUBLE_BOOKED + ) + else: + teacher_time_slot_tracking[ + (assigned_teacher, assigned_time_slot) + ] = section + + # Penalty 2: Double booking a classroom in the same time slot + if ( + assigned_classroom, + assigned_time_slot, + ) in classroom_time_slot_tracking: + section_fitness -= ( + PenaltyConstants.PENALTY_CLASSROOM_DOUBLE_BOOKED + ) + else: + classroom_time_slot_tracking[ + (assigned_classroom, assigned_time_slot) + ] = section + + # Penalty 3: Over-capacity classrooms + if section_strength > self.classroom_capacity.get( + assigned_classroom, Defaults.max_class_capacity + ): + section_fitness -= PenaltyConstants.PENALTY_OVER_CAPACITY + + # Penalty 4: Assigning teachers during unpreferred time slots + preferred_time_slots = self.teacher_time_preferences.get( + assigned_teacher, [] + ) + if assigned_time_slot not in preferred_time_slots: + section_fitness -= ( + PenaltyConstants.PENALTY_UN_PREFERRED_SLOT + ) + + # Penalty 5: Scheduling teacher on a non-duty day + assigned_day = day + if assigned_day not in TeacherWorkload.teacher_duty_days.get( + assigned_teacher, [] + ): + section_fitness -= PenaltyConstants.PENALTY_NON_DUTY_DAY + + # Tracking teacher workload + if assigned_teacher not in teacher_workload_tracking: + teacher_workload_tracking[assigned_teacher] = [] + teacher_workload_tracking[assigned_teacher].append( + assigned_time_slot + ) + + # Penalty 6: Exceeding teacher daily workload + for teacher, times_assigned in teacher_workload_tracking.items(): + if teacher is not None: + if len(times_assigned) > self.teacher_daily_workload.get( + teacher, 0 + ): + section_fitness -= ( + PenaltyConstants.PENALTY_OVERLOAD_TEACHER + ) + + daily_section_fitness_scores[week][day][section] = section_fitness + day_fitness += section_fitness + + weekly_fitness += day_fitness + + weekly_fitness_scores[week] = weekly_fitness + + return daily_section_fitness_scores, weekly_fitness_scores + + +if __name__ == "__main__": + total_sections = 6 + total_classrooms = 8 + total_labs = 3 + + # Generate timetable + timetable_generator = TimeTableGeneration( + teacher_subject_mapping=SubjectTeacherMap.subject_teacher_map, + total_sections=total_sections, + total_classrooms=total_classrooms, + total_labs=total_labs, + teacher_preferences=TeacherWorkload.teacher_preferences, + teacher_weekly_workload=TeacherWorkload.Weekly_workLoad, + special_subjects=SpecialSubjects.special_subjects, + subject_quota_limits=SubjectWeeklyQuota.subject_quota, + teacher_duty_days=TeacherWorkload.teacher_duty_days, + ) + generated_timetables = timetable_generator.create_timetable(5) + + # Evaluate fitness + fitness_evaluator = TimetableFitnessEvaluator( + generated_timetables, + timetable_generator.sections_manager.sections, + SubjectTeacherMap.subject_teacher_map, + timetable_generator.classrooms_manager.classrooms, + timetable_generator.classrooms_manager.labs, + timetable_generator.room_capacity_manager.room_capacity, + timetable_generator.room_capacity_manager.section_strength, + timetable_generator.subject_quota_limits, + timetable_generator.teacher_availability_preferences, + timetable_generator.weekly_workload, + ) + + ( + section_fitness_data, + weekly_fitness_data, + ) = fitness_evaluator.evaluate_timetable_fitness() + + # Save results + with open("GA/chromosome.json", "w") as timetable_file: + json.dump(generated_timetables, timetable_file, indent=4) + + fitness_output_data = { + "section_fitness_scores": section_fitness_data, + "weekly_fitness_scores": weekly_fitness_data, + } + + with open("GA/fitness.json", "w") as fitness_scores_file: + json.dump(fitness_output_data, fitness_scores_file, indent=4) + + print("Timetable and fitness scores have been saved.") diff --git a/build/lib/GA/mutation.py b/build/lib/GA/mutation.py new file mode 100644 index 0000000..9416ff4 --- /dev/null +++ b/build/lib/GA/mutation.py @@ -0,0 +1,38 @@ +import copy +import random + +from Constants.constant import Defaults + + +class TimeTableMutation: + def __init__(self, mutation_rate=0.7): + self.mutation_rate = mutation_rate + + def mutate_time_slots_in_section(self, schedule: dict, section: str) -> bool: + if section not in schedule or len(schedule[section]) < 2: + return False + + section_slots = schedule[section] + time_slots = [entry["time_slot"] for entry in section_slots] + random.shuffle(time_slots) + for i, entry in enumerate(section_slots): + entry["time_slot"] = time_slots[i] + return True + + def mutate_schedule_for_week(self, weekly_schedule: dict) -> dict: + mutated_schedule = copy.deepcopy(weekly_schedule) + for day, day_schedule in mutated_schedule.items(): + sections = list(day_schedule.keys()) + num_to_mutate = max(1, int(self.mutation_rate * len(sections))) + sections_to_mutate = random.sample(sections, num_to_mutate) + for section in sections_to_mutate: + self.mutate_time_slots_in_section(day_schedule, section) + return mutated_schedule + + +class TimeTableCrossOver: + def perform_crossover(self, timetable1: dict, timetable2: dict) -> tuple: + for day in Defaults.working_days: + if day in timetable1 and day in timetable2: + timetable1[day], timetable2[day] = timetable2[day], timetable1[day] + return timetable1, timetable2 diff --git a/build/lib/GA/selection.py b/build/lib/GA/selection.py new file mode 100644 index 0000000..b761136 --- /dev/null +++ b/build/lib/GA/selection.py @@ -0,0 +1,97 @@ +import random + + +class TimeTableSelection: + def __init__(self): + pass + + def select_chromosomes( + self, weekly_fitness_scores, top_percentage=0.20, roulette_percentage=0.10 + ): + """ + Entry point for selecting chromosomes. + Select chromosomes based on top scores and roulette-wheel selection. + """ + + if not weekly_fitness_scores: + print("Weekly fitness scores are empty. Ensure data is loaded correctly!") + return {} + + top_selected, remaining_scores = self.get_top_and_remaining_items( + weekly_fitness_scores, top_percentage + ) + + roulette_selected = self.roulette_wheel_selection( + remaining_scores, int(len(weekly_fitness_scores) * roulette_percentage) + ) + + selected_fitness = {**top_selected, **roulette_selected} + # self.display_selected_chromosomes(selected_fitness) + return selected_fitness + + @staticmethod + def calculate_cumulative_probabilities(scores): + """ + Calculate cumulative probabilities for roulette wheel selection. + """ + + cumulative_probabilities = [] + cumulative_sum = 0 + + for week, score in scores.items(): + cumulative_sum += score + cumulative_probabilities.append((cumulative_sum, week)) + + return cumulative_probabilities + + @staticmethod + def perform_roulette_selection(cumulative_probabilities, total_fitness, num_select): + """ + Perform roulette-wheel selection to choose items. + """ + + selected_items = [] + for _ in range(num_select): + rand_value = random.uniform(0, total_fitness) + for cumulative_sum, week in cumulative_probabilities: + if rand_value <= cumulative_sum: + selected_items.append(week) + break + + return selected_items + + def roulette_wheel_selection(self, scores, num_select): + """ + Select items using roulette-wheel selection. + """ + + if not scores: + print("Scores are empty. Cannot perform roulette selection.") + return {} + + total_fitness = sum(scores.values()) + cumulative_probabilities = self.calculate_cumulative_probabilities(scores) + selected_items = self.perform_roulette_selection( + cumulative_probabilities, total_fitness, num_select + ) + return {week: scores[week] for week in selected_items} + + @staticmethod + def get_top_and_remaining_items(scores, percentage): + num_select = max( + 1, int(len(scores) * percentage) + ) # at least 1 chromosome to select. + sorted_scores = sorted(scores.items(), key=lambda x: x[1], reverse=True) + top_selected = dict(sorted_scores[:num_select]) + remaining_items = dict(sorted_scores[num_select:]) + return top_selected, remaining_items + + @staticmethod + def display_selected_chromosomes(selected_fitness): + """ + Display the selected chromosomes and their fitness scores. + """ + + print("\n--- Selected Weeks and Fitness Scores ---") + for week, score in selected_fitness.items(): + print(f"Week: {week}, Score: {score}") diff --git a/build/lib/Samples/__init__.py b/build/lib/Samples/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/Samples/samples.py b/build/lib/Samples/samples.py new file mode 100644 index 0000000..ae5227a --- /dev/null +++ b/build/lib/Samples/samples.py @@ -0,0 +1,1821 @@ +class TimeSlots: + time_slots = { + 1: "9:00 - 9:55", + 2: "9:55 - 10:50", + 3: "11:10 - 12:05", + 4: "12:05 - 1:00", + 5: "1:20 - 2:15", + 6: "2:15 - 3:10", + 7: "3:30 - 4:25", + } + + +class SubjectTeacherMap: + subject_teacher_map = { + "TCS-531": ["AB01", "PK02"], + "TCS-502": ["SS03", "AA04", "AC05"], + "TCS-503": ["SP06", "DP07", "AC05"], + "PCS-506": ["AD08", "RD09"], + "TMA-502": ["BJ10", "RS11", "JM12", "NJ13"], + "PMA-502": ["PM14", "AD08", "AA15"], + "TCS-509": ["SJ16", "AB17", "HP18", "SG19"], + "XCS-501": ["DT20", "PA21", "NB22"], + "CSP-501": ["AK23"], + "SCS-501": ["AP24"], + "PCS-503": ["RS11", "DP07", "SP06", "VD25"], + "Placement_Class": ["AK26"], + "TCS-511": ["PK02"], + "TCS-592": ["AB01"], + "TCS-512": ["HP18"], + "TCS-519": ["DP07"], + "PCS-512": ["VD25"], + } + + +class WorkingDays: + days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] + + +class RoomCapacity: + room_capacity = {"R1": 200, "R2": 230, "R3": 240, "R4": 250, "R5": 250} + section_strength = {"A": 70, "B": 100, "C": 75, "D": 100} + lab_capacity = {"L1": 70, "L2": 50, "L3": 70, "L4": 50, "L5": 70, "L6": 50} + + +class SubjectWeeklyQuota: + subject_quota = { + "TCS-531": 3, + "TCS-502": 3, + "TCS-503": 3, + "PCS-506": 1, + "TMA-502": 3, + "PMA-502": 1, + "TCS-509": 3, + "XCS-501": 2, + "CSP-501": 1, + "SCS-501": 1, + "PCS-503": 1, + "Placement_Class": 1, + } + + +class SpecialSubjects: + special_subjects = ["Placement_Class"] + Labs = ["PCS-506", "PCS-503", "PMA-502"] + + +class PenaltyConstants: + PENALTY_TEACHER_DOUBLE_BOOKED = 30 + PENALTY_CLASSROOM_DOUBLE_BOOKED = 20 + PENALTY_OVER_CAPACITY = 25 + PENALTY_UN_PREFERRED_SLOT = 5 + PENALTY_OVERLOAD_TEACHER = 10 + + +class TeacherWorkload: + Weekly_workLoad = { + "AB01": 5, + "PK02": 5, + "SS03": 5, + "AA04": 5, + "AC05": 5, + "SP06": 5, + "DP07": 5, + "AD08": 5, + "RD09": 5, + "BJ10": 5, + "RS11": 5, + "JM12": 5, + "NJ13": 5, + "PM14": 5, + "AA15": 5, + "SJ16": 5, + "AB17": 5, + "HP18": 5, + "SG19": 5, + "DT20": 5, + "PA21": 5, + "NB22": 5, + "AK23": 5, + "AP24": 5, + "VD25": 5, + "AK26": 5, + } + + teacher_preferences = { + "AB01": [1], + "PK02": [1, 2, 3, 4, 5, 6, 7], + "SS03": [1, 2, 3, 4, 5, 6, 7], + "AA04": [1, 2, 3, 4, 5, 6, 7], + "AC05": [1, 2, 3, 4, 5, 6, 7], + "SP06": [1, 2, 3, 4, 5, 6, 7], + "DP07": [1, 2, 3, 4, 5, 6, 7], + "AD08": [1, 2, 3, 4, 5, 6, 7], + "RD09": [1, 2, 3, 4, 5, 6, 7], + "BJ10": [1, 2, 3, 4, 5, 6, 7], + "RS11": [1, 2, 3, 4, 5, 6, 7], + "JM12": [1, 2, 3, 4, 5, 6, 7], + "NJ13": [1, 2, 3, 4, 5, 6, 7], + "PM14": [1, 2, 3, 4, 5, 6, 7], + "AA15": [1, 2, 3, 4, 5, 6, 7], + "SJ16": [1, 2, 3, 4, 5, 6, 7], + "AB17": [1, 2, 3, 4, 5, 6, 7], + "HP18": [1, 2, 3, 4, 5, 6, 7], + "SG19": [1, 2, 3, 4, 5, 6, 7], + "DT20": [1, 2, 3, 4, 5, 6, 7], + "PA21": [1, 2, 3, 4, 5, 6, 7], + "NB22": [1, 2, 3, 4, 5, 6, 7], + "AK23": [1, 2, 3, 4, 5, 6, 7], + "AP24": [1, 2, 3, 4, 5, 6, 7], + "VD25": [1, 2, 3, 4, 5, 6, 7], + "AK26": [5], + } + teacher_duty_days = { + "AB01": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + "PK02": ["Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], + "SS03": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + "AA04": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + "AC05": ["Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], + "SP06": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + "DP07": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + "AD08": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + "RD09": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + "BJ10": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + "RS11": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + "JM12": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + "NJ13": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + "PM14": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + "AA15": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + "SJ16": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + "AB17": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + "HP18": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + "SG19": ["Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], + "DT20": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + "PA21": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + "NB22": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], + "AK23": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], + "AP24": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], + "VD25": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], + "AK26": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], + } + + +class InterDepartment: + teacher_availability_matrix = { + "AB01": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "PK02": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "SS03": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "AA04": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "AC05": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "SP06": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "DP07": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "AD08": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "RD09": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "BJ10": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "RS11": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "JM12": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "NJ13": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "PM14": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "AA15": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "SJ16": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "AB17": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "HP18": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "SG19": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "DT20": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "PA21": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "NB22": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "AK23": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "AP24": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "VD25": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "AK26": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + } + + +class Lab_availability: + lab_availability_matrix = { + "L1": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "L2": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "L3": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "L4": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "L5": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + "L6": [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + ], + } + + +class SampleChromosome: + schedule1 = { + "Monday": { + "A": [ + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R1", + "time_slot": "3:30 - 4:25", + }, + ], + "B": [ + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L4", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L4", + "time_slot": "11:10 - 12:05", + }, + ], + "C": [ + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00", + }, + ], + "D": [ + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "SG19", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25", + }, + ], + }, + "Tuesday": { + "A": [ + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "11:10 - 12:05", + }, + ], + "B": [ + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "3:30 - 4:25", + }, + ], + "C": [ + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "NB22", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25", + }, + ], + "D": [ + { + "teacher_id": "SG19", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + ], + }, + "Wednesday": { + "A": [ + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L5", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L5", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "3:30 - 4:25", + }, + ], + "B": [ + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + ], + "C": [ + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "L2", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "L2", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25", + }, + ], + "D": [ + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + ], + }, + "Thursday": { + "A": [ + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00", + }, + ], + "B": [ + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "3:30 - 4:25", + }, + ], + "C": [ + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L4", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L4", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "L5", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "L5", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25", + }, + ], + "D": [ + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + ], + }, + "Friday": { + "A": [ + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00", + }, + ], + "B": [ + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "3:30 - 4:25", + }, + ], + "C": [ + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L5", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L5", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25", + }, + ], + "D": [ + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + ], + }, + } + schedule2 = { + "Monday": { + "A": [ + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "3:30 - 4:25", + }, + ], + "B": [ + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "3:30 - 4:25", + }, + ], + "C": [ + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RD09", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L4", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L4", + "time_slot": "11:10 - 12:05", + }, + ], + "D": [ + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "NB22", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + ], + }, + "Tuesday": { + "A": [ + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AK26", + "subject_id": "Placement_Class", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "3:30 - 4:25", + }, + ], + "B": [ + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + ], + "C": [ + { + "teacher_id": "NB22", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R3", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25", + }, + ], + "D": [ + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + ], + }, + "Wednesday": { + "A": [ + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "11:10 - 12:05", + }, + ], + "B": [ + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + ], + "C": [ + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "DP07", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25", + }, + ], + "D": [ + { + "teacher_id": "SP06", + "subject_id": "PCS-503", + "classroom_id": "L4", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "SP06", + "subject_id": "PCS-503", + "classroom_id": "L4", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "NB22", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R4", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R4", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "3:30 - 4:25", + }, + ], + }, + "Thursday": { + "A": [ + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R1", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "3:30 - 4:25", + }, + ], + "B": [ + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "RS11", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + ], + "C": [ + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "JM12", + "subject_id": "TMA-502", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25", + }, + ], + "D": [ + { + "teacher_id": "NJ13", + "subject_id": "TMA-502", + "classroom_id": "R4", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AP24", + "subject_id": "SCS-501", + "classroom_id": "R4", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "PA21", + "subject_id": "XCS-501", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + ], + }, + "Friday": { + "A": [ + { + "teacher_id": "SJ16", + "subject_id": "TCS-509", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R1", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "SP06", + "subject_id": "TCS-503", + "classroom_id": "R1", + "time_slot": "12:05 - 1:00", + }, + ], + "B": [ + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "RS11", + "subject_id": "PCS-503", + "classroom_id": "L1", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "BJ10", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "PK02", + "subject_id": "TCS-531", + "classroom_id": "R2", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "DP07", + "subject_id": "TCS-503", + "classroom_id": "R2", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R2", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "SS03", + "subject_id": "TCS-502", + "classroom_id": "R2", + "time_slot": "3:30 - 4:25", + }, + ], + "C": [ + { + "teacher_id": "AC05", + "subject_id": "TCS-503", + "classroom_id": "R3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "AK23", + "subject_id": "CSP-501", + "classroom_id": "R3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AB17", + "subject_id": "TCS-509", + "classroom_id": "R3", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AB01", + "subject_id": "TCS-531", + "classroom_id": "R3", + "time_slot": "12:05 - 1:00", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "2:15 - 3:10", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "1:20 - 2:15", + }, + { + "teacher_id": "DT20", + "subject_id": "XCS-501", + "classroom_id": "R3", + "time_slot": "3:30 - 4:25", + }, + ], + "D": [ + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L3", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "PM14", + "subject_id": "PMA-502", + "classroom_id": "L3", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "HP18", + "subject_id": "TCS-509", + "classroom_id": "R4", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "AA04", + "subject_id": "TCS-502", + "classroom_id": "R4", + "time_slot": "12:05 - 1:00", + }, + ], + }, + } diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs new file mode 100644 index 0000000..c0f1d02 --- /dev/null +++ b/frontend/.eslintrc.cjs @@ -0,0 +1,21 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:react/recommended', + 'plugin:react/jsx-runtime', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, + settings: { react: { version: '18.2' } }, + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + 'react/prop-types': 'off', + }, +} diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..117b7d7 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + Timetable Management System + + +
+ + + \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..0677499 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,6259 @@ +{ + "name": "timetable-frontend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "timetable-frontend", + "version": "1.0.0", + "dependencies": { + "@tanstack/react-query": "^5.12.2", + "axios": "^1.6.2", + "clsx": "^2.0.0", + "date-fns": "^2.30.0", + "lucide-react": "^0.294.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.20.0", + "recharts": "^2.10.3", + "tailwind-merge": "^2.1.0", + "xlsx": "^0.18.5", + "zustand": "^4.4.7" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.2.1", + "autoprefixer": "^10.4.16", + "eslint": "^8.54.0", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.4", + "postcss": "^8.4.32", + "tailwindcss": "^3.3.6", + "vite": "^5.0.8" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.28.6.tgz", + "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.28.6.tgz", + "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.28.6.tgz", + "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.28.6.tgz", + "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.6.tgz", + "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.6" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.28.6.tgz", + "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.6.tgz", + "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@remix-run/router": { + "version": "1.23.2", + "resolved": "https://registry.npmmirror.com/@remix-run/router/-/router-1.23.2.tgz", + "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.0.tgz", + "integrity": "sha512-tPgXB6cDTndIe1ah7u6amCI1T0SsnlOuKgg10Xh3uizJk4e5M1JGaUMk7J4ciuAUcFpbOiNhm2XIjP9ON0dUqA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.0.tgz", + "integrity": "sha512-sa4LyseLLXr1onr97StkU1Nb7fWcg6niokTwEVNOO7awaKaoRObQ54+V/hrF/BP1noMEaaAW6Fg2d/CfLiq3Mg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.0.tgz", + "integrity": "sha512-/NNIj9A7yLjKdmkx5dC2XQ9DmjIECpGpwHoGmA5E1AhU0fuICSqSWScPhN1yLCkEdkCwJIDu2xIeLPs60MNIVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.0.tgz", + "integrity": "sha512-xoh8abqgPrPYPr7pTYipqnUi1V3em56JzE/HgDgitTqZBZ3yKCWI+7KUkceM6tNweyUKYru1UMi7FC060RyKwA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.0.tgz", + "integrity": "sha512-PCkMh7fNahWSbA0OTUQ2OpYHpjZZr0hPr8lId8twD7a7SeWrvT3xJVyza+dQwXSSq4yEQTMoXgNOfMCsn8584g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.0.tgz", + "integrity": "sha512-1j3stGx+qbhXql4OCDZhnK7b01s6rBKNybfsX+TNrEe9JNq4DLi1yGiR1xW+nL+FNVvI4D02PUnl6gJ/2y6WJA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.0.tgz", + "integrity": "sha512-eyrr5W08Ms9uM0mLcKfM/Uzx7hjhz2bcjv8P2uynfj0yU8GGPdz8iYrBPhiLOZqahoAMB8ZiolRZPbbU2MAi6Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.0.tgz", + "integrity": "sha512-Xds90ITXJCNyX9pDhqf85MKWUI4lqjiPAipJ8OLp8xqI2Ehk+TCVhF9rvOoN8xTbcafow3QOThkNnrM33uCFQA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.0.tgz", + "integrity": "sha512-Xws2KA4CLvZmXjy46SQaXSejuKPhwVdaNinldoYfqruZBaJHqVo6hnRa8SDo9z7PBW5x84SH64+izmldCgbezw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.0.tgz", + "integrity": "sha512-hrKXKbX5FdaRJj7lTMusmvKbhMJSGWJ+w++4KmjiDhpTgNlhYobMvKfDoIWecy4O60K6yA4SnztGuNTQF+Lplw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.0.tgz", + "integrity": "sha512-6A+nccfSDGKsPm00d3xKcrsBcbqzCTAukjwWK6rbuAnB2bHaL3r9720HBVZ/no7+FhZLz/U3GwwZZEh6tOSI8Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.0.tgz", + "integrity": "sha512-4P1VyYUe6XAJtQH1Hh99THxr0GKMMwIXsRNOceLrJnaHTDgk1FTcTimDgneRJPvB3LqDQxUmroBclQ1S0cIJwQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.0.tgz", + "integrity": "sha512-8Vv6pLuIZCMcgXre6c3nOPhE0gjz1+nZP6T+hwWjr7sVH8k0jRkH+XnfjjOTglyMBdSKBPPz54/y1gToSKwrSQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.0.tgz", + "integrity": "sha512-r1te1M0Sm2TBVD/RxBPC6RZVwNqUTwJTA7w+C/IW5v9Ssu6xmxWEi+iJQlpBhtUiT1raJ5b48pI8tBvEjEFnFA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.0.tgz", + "integrity": "sha512-say0uMU/RaPm3CDQLxUUTF2oNWL8ysvHkAjcCzV2znxBr23kFfaxocS9qJm+NdkRhF8wtdEEAJuYcLPhSPbjuQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.0.tgz", + "integrity": "sha512-/MU7/HizQGsnBREtRpcSbSV1zfkoxSTR7wLsRmBPQ8FwUj5sykrP1MyJTvsxP5KBq9SyE6kH8UQQQwa0ASeoQQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.0.tgz", + "integrity": "sha512-Q9eh+gUGILIHEaJf66aF6a414jQbDnn29zeu0eX3dHMuysnhTvsUvZTCAyZ6tJhUjnvzBKE4FtuaYxutxRZpOg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.0.tgz", + "integrity": "sha512-OR5p5yG5OKSxHReWmwvM0P+VTPMwoBS45PXTMYaskKQqybkS3Kmugq1W+YbNWArF8/s7jQScgzXUhArzEQ7x0A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.0.tgz", + "integrity": "sha512-XeatKzo4lHDsVEbm1XDHZlhYZZSQYym6dg2X/Ko0kSFgio+KXLsxwJQprnR48GvdIKDOpqWqssC3iBCjoMcMpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.0.tgz", + "integrity": "sha512-Lu71y78F5qOfYmubYLHPcJm74GZLU6UJ4THkf/a1K7Tz2ycwC2VUbsqbJAXaR6Bx70SRdlVrt2+n5l7F0agTUw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.0.tgz", + "integrity": "sha512-v5xwKDWcu7qhAEcsUubiav7r+48Uk/ENWdr82MBZZRIm7zThSxCIVDfb3ZeRRq9yqk+oIzMdDo6fCcA5DHfMyA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.0.tgz", + "integrity": "sha512-XnaaaSMGSI6Wk8F4KK3QP7GfuuhjGchElsVerCplUuxRIzdvZ7hRBpLR0omCmw+kI2RFJB80nenhOoGXlJ5TfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.0.tgz", + "integrity": "sha512-3K1lP+3BXY4t4VihLw5MEg6IZD3ojSYzqzBG571W3kNQe4G4CcFpSUQVgurYgib5d+YaCjeFow8QivWp8vuSvA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.0.tgz", + "integrity": "sha512-MDk610P/vJGc5L5ImE4k5s+GZT3en0KoK1MKPXCRgzmksAMk79j4h3k1IerxTNqwDLxsGxStEZVBqG0gIqZqoA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.0.tgz", + "integrity": "sha512-Zv7v6q6aV+VslnpwzqKAmrk5JdVkLUzok2208ZXGipjb+msxBr/fJPZyeEXiFgH7k62Ak0SLIfxQRZQvTuf7rQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tanstack/query-core": { + "version": "5.90.20", + "resolved": "https://registry.npmmirror.com/@tanstack/query-core/-/query-core-5.90.20.tgz", + "integrity": "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.90.20", + "resolved": "https://registry.npmmirror.com/@tanstack/react-query/-/react-query-5.90.20.tgz", + "integrity": "sha512-vXBxa+qeyveVO7OA0jX1z+DeyCA4JKnThKv411jd5SORpBKgkcVnYKCiBgECvADvniBX7tobwBmg01qq9JmMJw==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.90.20" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmmirror.com/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmmirror.com/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmmirror.com/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmmirror.com/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmmirror.com/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmmirror.com/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.23", + "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.23.tgz", + "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001760", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.13.4", + "resolved": "https://registry.npmmirror.com/axios/-/axios-1.13.4.tgz", + "integrity": "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.18", + "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.18.tgz", + "integrity": "sha512-e23vBV1ZLfjb9apvfPk4rHVu2ry6RIr2Wfs+O324okSidrX7pTAnEJPCh/O5BtRlr7QtZI7ktOP3vsqr7Z5XoA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001766", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz", + "integrity": "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmmirror.com/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.279", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.279.tgz", + "integrity": "sha512-0bblUU5UNdOt5G7XqGiJtpZMONma6WAfq9vsFmtn9x1+joAObr6x1chfqyxFSDCAFwFhCQDrqeAr6MYdpwJ9Hg==", + "dev": true, + "license": "ISC" + }, + "node_modules/es-abstract": { + "version": "1.24.1", + "resolved": "https://registry.npmmirror.com/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz", + "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.1", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.1.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.3.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.5", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmmirror.com/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmmirror.com/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.2", + "resolved": "https://registry.npmmirror.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.26", + "resolved": "https://registry.npmmirror.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", + "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-equals": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/fast-equals/-/fast-equals-5.4.0.tgz", + "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmmirror.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmmirror.com/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmmirror.com/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmmirror.com/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmmirror.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.294.0", + "resolved": "https://registry.npmmirror.com/lucide-react/-/lucide-react-0.294.0.tgz", + "integrity": "sha512-V7o0/VECSGbLHn3/1O67FUgBwWB+hmzshrgDVRJQhMh8uj5D3HBuIvhuAmQTtlupILSplwIZg5FTc4tTKMA2SA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmmirror.com/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmmirror.com/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmmirror.com/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmmirror.com/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmmirror.com/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-import/node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmmirror.com/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmmirror.com/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmmirror.com/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "6.30.3", + "resolved": "https://registry.npmmirror.com/react-router/-/react-router-6.30.3.tgz", + "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.3", + "resolved": "https://registry.npmmirror.com/react-router-dom/-/react-router-dom-6.30.3.tgz", + "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.2", + "react-router": "6.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-smooth": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/react-smooth/-/react-smooth-4.0.4.tgz", + "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", + "license": "MIT", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmmirror.com/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recharts": { + "version": "2.15.4", + "resolved": "https://registry.npmmirror.com/recharts/-/recharts-2.15.4.tgz", + "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^18.3.1", + "react-smooth": "^4.0.4", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmmirror.com/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "license": "MIT", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/recharts/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmmirror.com/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmmirror.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmmirror.com/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.57.0", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.57.0.tgz", + "integrity": "sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.57.0", + "@rollup/rollup-android-arm64": "4.57.0", + "@rollup/rollup-darwin-arm64": "4.57.0", + "@rollup/rollup-darwin-x64": "4.57.0", + "@rollup/rollup-freebsd-arm64": "4.57.0", + "@rollup/rollup-freebsd-x64": "4.57.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.0", + "@rollup/rollup-linux-arm-musleabihf": "4.57.0", + "@rollup/rollup-linux-arm64-gnu": "4.57.0", + "@rollup/rollup-linux-arm64-musl": "4.57.0", + "@rollup/rollup-linux-loong64-gnu": "4.57.0", + "@rollup/rollup-linux-loong64-musl": "4.57.0", + "@rollup/rollup-linux-ppc64-gnu": "4.57.0", + "@rollup/rollup-linux-ppc64-musl": "4.57.0", + "@rollup/rollup-linux-riscv64-gnu": "4.57.0", + "@rollup/rollup-linux-riscv64-musl": "4.57.0", + "@rollup/rollup-linux-s390x-gnu": "4.57.0", + "@rollup/rollup-linux-x64-gnu": "4.57.0", + "@rollup/rollup-linux-x64-musl": "4.57.0", + "@rollup/rollup-openbsd-x64": "4.57.0", + "@rollup/rollup-openharmony-arm64": "4.57.0", + "@rollup/rollup-win32-arm64-msvc": "4.57.0", + "@rollup/rollup-win32-ia32-msvc": "4.57.0", + "@rollup/rollup-win32-x64-gnu": "4.57.0", + "@rollup/rollup-win32-x64-msvc": "4.57.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmmirror.com/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmmirror.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmmirror.com/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwind-merge": { + "version": "2.6.0", + "resolved": "https://registry.npmmirror.com/tailwind-merge/-/tailwind-merge-2.6.0.tgz", + "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmmirror.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmmirror.com/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmmirror.com/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..37f8747 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,37 @@ +{ + "name": "timetable-frontend", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0" + }, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.20.0", + "axios": "^1.6.2", + "@tanstack/react-query": "^5.12.2", + "zustand": "^4.4.7", + "lucide-react": "^0.294.0", + "clsx": "^2.0.0", + "tailwind-merge": "^2.1.0", + "date-fns": "^2.30.0", + "recharts": "^2.10.3", + "xlsx": "^0.18.5" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.2.1", + "autoprefixer": "^10.4.16", + "eslint": "^8.54.0", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.4", + "postcss": "^8.4.32", + "tailwindcss": "^3.3.6", + "vite": "^5.0.8" + } +} diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/frontend/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx new file mode 100644 index 0000000..418c83c --- /dev/null +++ b/frontend/src/App.jsx @@ -0,0 +1,46 @@ +import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import Login from './pages/Login' +import MasterAdminDashboard from './pages/master-admin/Dashboard' +import CollegeDashboard from './pages/college/Dashboard' +import ProtectedRoute from './components/ProtectedRoute' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + retry: 1, + }, + }, +}) + +function App() { + return ( + + + + } /> + + + + } + /> + + + + } + /> + } /> + + + + ) +} + +export default App diff --git a/frontend/src/components/ProtectedRoute.jsx b/frontend/src/components/ProtectedRoute.jsx new file mode 100644 index 0000000..c192294 --- /dev/null +++ b/frontend/src/components/ProtectedRoute.jsx @@ -0,0 +1,16 @@ +import { Navigate } from 'react-router-dom' +import { useAuthStore } from '../stores/authStore' + +export default function ProtectedRoute({ children, allowedRoles }) { + const { user, isAuthenticated } = useAuthStore() + + if (!isAuthenticated()) { + return + } + + if (user && !allowedRoles.includes(user.role)) { + return + } + + return <>{children} +} diff --git a/frontend/src/index.css b/frontend/src/index.css new file mode 100644 index 0000000..fe0fd6d --- /dev/null +++ b/frontend/src/index.css @@ -0,0 +1,31 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light; + color: #0f172a; + background-color: #f8fafc; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + margin: 0; + min-width: 320px; + min-height: 100vh; + background-color: #f8fafc; + color: #0f172a; +} + +#root { + width: 100%; + min-height: 100vh; +} \ No newline at end of file diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx new file mode 100644 index 0000000..54b39dd --- /dev/null +++ b/frontend/src/main.jsx @@ -0,0 +1,10 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.jsx' +import './index.css' + +ReactDOM.createRoot(document.getElementById('root')).render( + + + , +) diff --git a/frontend/src/pages/Login.jsx b/frontend/src/pages/Login.jsx new file mode 100644 index 0000000..150f27a --- /dev/null +++ b/frontend/src/pages/Login.jsx @@ -0,0 +1,96 @@ +import { useState } from 'react' +import { useNavigate } from 'react-router-dom' +import { useAuthStore } from '../stores/authStore' +import api from '../lib/api' + +export default function Login() { + const [email, setEmail] = useState('') + const [password, setPassword] = useState('') + const [error, setError] = useState('') + const [loading, setLoading] = useState(false) + + const navigate = useNavigate() + const setAuth = useAuthStore((state) => state.setAuth) + + const handleSubmit = async (e) => { + e.preventDefault() + setError('') + setLoading(true) + + try { + const { data } = await api.post('/auth/login/', { email, password }) + const { user, access, refresh } = data + + setAuth(user, access, refresh) + + navigate(user.role === 'master_admin' ? '/master-admin' : '/college') + } catch (err) { + setError(err.response?.data?.error || 'Invalid credentials') + } finally { + setLoading(false) + } + } + + return ( +
+
+
+

+ College Timetable System +

+

+ Administrative Login +

+
+ +
+ {error && ( +
+ {error} +
+ )} + +
+ + setEmail(e.target.value)} + required + className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-600" + placeholder="admin@college.edu" + /> +
+ +
+ + setPassword(e.target.value)} + required + className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-600" + placeholder="••••••••" + /> +
+ + +
+ +
+ Authorized personnel only +
+
+
+ ) +} \ No newline at end of file diff --git a/frontend/src/pages/college/Dashboard.jsx b/frontend/src/pages/college/Dashboard.jsx new file mode 100644 index 0000000..e961317 --- /dev/null +++ b/frontend/src/pages/college/Dashboard.jsx @@ -0,0 +1,150 @@ +import { Routes, Route, Link, useNavigate } from 'react-router-dom' +import { useQuery } from '@tanstack/react-query' +import { useAuthStore } from '../../stores/authStore' +import Timetables from './Timetables' +import TimetableDetail from './TimetableDetail' +import GenerateTimetable from './GenerateTimetable' +import Resources from './Resources' +import { LogOut, Calendar, Plus, Settings, Home, Building, Users, BookOpen, GraduationCap } from 'lucide-react' +import api from '../../lib/api' + +export default function CollegeDashboard() { + const { user, logout } = useAuthStore() + const navigate = useNavigate() + + const handleLogout = () => { + logout() + navigate('/login') + } + + return ( +
+ + +
+ + } /> + } /> + } /> + } /> + } /> + +
+
+ ) +} + +function DashboardHome() { + const { data: stats, isLoading } = useQuery({ + queryKey: ['dashboard-stats'], + queryFn: async () => { + try { + const [timetables, departments, teachers, subjects, sections, classrooms] = await Promise.all([ + api.get('/timetables/').then(r => r.data.results || r.data || []).catch(() => []), + api.get('/departments/').then(r => r.data.results || r.data || []).catch(() => []), + api.get('/teachers/').then(r => r.data.results || r.data || []).catch(() => []), + api.get('/subjects/').then(r => r.data.results || r.data || []).catch(() => []), + api.get('/sections/').then(r => r.data.results || r.data || []).catch(() => []), + api.get('/classrooms/').then(r => r.data.results || r.data || []).catch(() => []), + ]) + return { + timetables: timetables?.length || 0, + departments: departments?.length || 0, + teachers: teachers?.length || 0, + subjects: subjects?.length || 0, + sections: sections?.length || 0, + classrooms: classrooms?.length || 0, + } + } catch (error) { + return { + timetables: 0, + departments: 0, + teachers: 0, + subjects: 0, + sections: 0, + classrooms: 0, + } + } + }, + }) + + if (isLoading) return
Loading...
+ + return ( +
+

Dashboard Overview

+
+ + + + + + +
+
+ ) +} + +function StatCard({ title, value, icon: Icon }) { + return ( +
+
+
+

{title}

+

{value}

+
+ +
+
+ ) +} diff --git a/frontend/src/pages/college/GenerateTimetable.jsx b/frontend/src/pages/college/GenerateTimetable.jsx new file mode 100644 index 0000000..0f1ad5d --- /dev/null +++ b/frontend/src/pages/college/GenerateTimetable.jsx @@ -0,0 +1,223 @@ +import { useState } from 'react' +import { useMutation, useQuery } from '@tanstack/react-query' +import { useNavigate } from 'react-router-dom' +import { Link } from 'react-router-dom' +import api from '../../lib/api' +import { AlertCircle, CheckCircle } from 'lucide-react' + +export default function GenerateTimetable() { + const [formData, setFormData] = useState({ + name: '', + academic_year: '', + semester: '', + total_generations: 50, + }) + const navigate = useNavigate() + + // Fetch resources to check availability + const { data: resources, isLoading: resourcesLoading } = useQuery({ + queryKey: ['generation-resources'], + queryFn: async () => { + try { + const [departments, teachers, subjects, sections, classrooms, mappings] = await Promise.all([ + api.get('/departments/').then(r => r.data.results || r.data || []).catch(() => []), + api.get('/teachers/').then(r => r.data.results || r.data || []).catch(() => []), + api.get('/subjects/').then(r => r.data.results || r.data || []).catch(() => []), + api.get('/sections/').then(r => r.data.results || r.data || []).catch(() => []), + api.get('/classrooms/').then(r => r.data.results || r.data || []).catch(() => []), + api.get('/subject-teacher-mappings/').then(r => r.data.results || r.data || []).catch(() => []), + ]) + return { + departments: departments?.length || 0, + teachers: teachers?.length || 0, + subjects: subjects?.length || 0, + sections: sections?.length || 0, + classrooms: classrooms?.length || 0, + mappings: mappings?.length || 0, + } + } catch (error) { + return { + departments: 0, + teachers: 0, + subjects: 0, + sections: 0, + classrooms: 0, + mappings: 0, + } + } + }, + }) + + const generateMutation = useMutation({ + mutationFn: (data) => api.post('/generate-timetable/generate/', data), + onSuccess: () => { + navigate('/college/timetables') + }, + }) + + const handleSubmit = (e) => { + e.preventDefault() + generateMutation.mutate(formData) + } + + const canGenerate = resources && + resources.departments > 0 && + resources.teachers > 0 && + resources.subjects > 0 && + resources.sections > 0 && + resources.classrooms > 0 && + resources.mappings > 0 + + return ( +
+

Generate Timetable

+ + {/* Resource Status */} +
+

Resource Status

+ {resourcesLoading ? ( +
Loading resources...
+ ) : ( +
+ + + + + + +
+ )} + + {!canGenerate && ( +
+
+ +
+

+ Missing required resources to generate timetable +

+

+ Please add all required resources in the Resources section. +

+
+
+
+ )} +
+ + {/* Generation Form */} +
+
+ + setFormData({ ...formData, name: e.target.value })} + required + className="w-full px-3 py-2 border rounded-md focus:ring-2 focus:ring-primary-500" + placeholder="e.g., Fall 2024 Timetable" + /> +
+
+ + setFormData({ ...formData, academic_year: e.target.value })} + required + className="w-full px-3 py-2 border rounded-md focus:ring-2 focus:ring-primary-500" + placeholder="e.g., 2024-2025" + /> +
+
+ + setFormData({ ...formData, semester: e.target.value })} + className="w-full px-3 py-2 border rounded-md focus:ring-2 focus:ring-primary-500" + placeholder="e.g., Fall" + /> +
+
+ + + setFormData({ ...formData, total_generations: parseInt(e.target.value) || 50 }) + } + min="1" + max="200" + className="w-full px-3 py-2 border rounded-md focus:ring-2 focus:ring-primary-500" + /> +

Higher values may take longer but produce better results

+
+ {generateMutation.error && ( +
+ {generateMutation.error?.response?.data?.error || generateMutation.error.message || 'Failed to generate timetable'} +
+ )} + +
+
+ ) +} + +function ResourceStatus({ label, count, required }) { + const hasResource = count > 0 + return ( +
+
+ {hasResource ? ( + + ) : ( + + )} + {label} + {required && *} +
+ + {count} + +
+ ) +} diff --git a/frontend/src/pages/college/Resources.jsx b/frontend/src/pages/college/Resources.jsx new file mode 100644 index 0000000..182d6e0 --- /dev/null +++ b/frontend/src/pages/college/Resources.jsx @@ -0,0 +1,1347 @@ +import { useState } from 'react' +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' +import api from '../../lib/api' +import { Plus, Edit, Trash2, Users, BookOpen, Building, GraduationCap, Settings, X } from 'lucide-react' + +export default function Resources() { + const [activeTab, setActiveTab] = useState('departments') + + const tabs = [ + { id: 'departments', label: 'Departments', icon: Building }, + { id: 'teachers', label: 'Teachers', icon: Users }, + { id: 'subjects', label: 'Subjects', icon: BookOpen }, + { id: 'sections', label: 'Sections', icon: GraduationCap }, + { id: 'classrooms', label: 'Classrooms', icon: Building }, + { id: 'mappings', label: 'Subject-Teacher', icon: Settings }, + { id: 'preferences', label: 'Teacher Preferences', icon: Settings }, + ] + + return ( +
+

Resource Management

+ +
+ +
+ +
+ {activeTab === 'departments' && } + {activeTab === 'teachers' && } + {activeTab === 'subjects' && } + {activeTab === 'sections' && } + {activeTab === 'classrooms' && } + {activeTab === 'mappings' && } + {activeTab === 'preferences' && } +
+
+ ) +} + +// Modal Component +function Modal({ title, children, onClose }) { + return ( +
+
+
+

{title}

+ +
+ {children} +
+
+ ) +} + +// Departments Tab +function DepartmentsTab() { + const [showModal, setShowModal] = useState(false) + const [editingItem, setEditingItem] = useState(null) + const queryClient = useQueryClient() + + const { data: departments, isLoading } = useQuery({ + queryKey: ['departments'], + queryFn: async () => { + const response = await api.get('/departments/') + return response.data.results || response.data + }, + }) + + const createMutation = useMutation({ + mutationFn: (data) => api.post('/departments/', data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['departments'] }) + setShowModal(false) + setEditingItem(null) + }, + }) + + const updateMutation = useMutation({ + mutationFn: ({ id, data }) => api.patch(`/departments/${id}/`, data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['departments'] }) + setShowModal(false) + setEditingItem(null) + }, + }) + + const deleteMutation = useMutation({ + mutationFn: (id) => api.delete(`/departments/${id}/`), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['departments'] }) + }, + }) + + const handleSubmit = (e) => { + e.preventDefault() + const formData = new FormData(e.currentTarget) + const data = { + name: formData.get('name'), + code: formData.get('code'), + } + if (editingItem) { + updateMutation.mutate({ id: editingItem.id, data }) + } else { + createMutation.mutate(data) + } + } + + if (isLoading) return
Loading...
+ + return ( +
+
+

Departments

+ +
+ +
+ + + + + + + + + + {departments?.map((dept) => ( + + + + + + ))} + +
NameCodeActions
{dept.name}{dept.code} + + +
+
+ + {showModal && ( + { + setShowModal(false) + setEditingItem(null) + }} + > +
+
+ + +
+
+ + +
+
+ + +
+
+
+ )} +
+ ) +} + +// Teachers Tab +function TeachersTab() { + const [showModal, setShowModal] = useState(false) + const [editingItem, setEditingItem] = useState(null) + const queryClient = useQueryClient() + + const { data: teachers, isLoading } = useQuery({ + queryKey: ['teachers'], + queryFn: async () => { + const response = await api.get('/teachers/') + return response.data.results || response.data + }, + }) + + const { data: departments } = useQuery({ + queryKey: ['departments'], + queryFn: async () => { + const response = await api.get('/departments/') + return response.data.results || response.data + }, + }) + + const createMutation = useMutation({ + mutationFn: (data) => api.post('/teachers/', data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['teachers'] }) + setShowModal(false) + }, + }) + + const deleteMutation = useMutation({ + mutationFn: (id) => api.delete(`/teachers/${id}/`), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['teachers'] }) + }, + }) + + const handleSubmit = async (e) => { + e.preventDefault() + const formData = new FormData(e.currentTarget) + + // First create user, then teacher + const userData = { + email: formData.get('email'), + password: formData.get('password'), + password2: formData.get('password'), + first_name: formData.get('first_name'), + last_name: formData.get('last_name'), + role: 'faculty', + } + + try { + // Register user first + const userResponse = await api.post('/auth/register/', userData) + const userId = userResponse.data.user.id + + // Then create teacher + const teacherData = { + user: userId, + employee_id: formData.get('employee_id'), + department: formData.get('department'), + designation: formData.get('designation'), + max_weekly_hours: parseInt(formData.get('max_weekly_hours')) || 20, + } + + createMutation.mutate(teacherData) + } catch (error) { + console.error('Error creating teacher:', error) + } + } + + if (isLoading) return
Loading...
+ + return ( +
+
+

Teachers

+ +
+ +
+ + + + + + + + + + + + {teachers?.map((teacher) => ( + + + + + + + + ))} + +
NameEmployee IDDepartmentDesignationActions
{teacher.user_name}{teacher.employee_id}{teacher.department_name}{teacher.designation} + +
+
+ + {showModal && ( + { + setShowModal(false) + setEditingItem(null) + }} + > +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ )} +
+ ) +} + +// Subjects Tab +function SubjectsTab() { + const [showModal, setShowModal] = useState(false) + const [editingItem, setEditingItem] = useState(null) + const queryClient = useQueryClient() + + const { data: subjects, isLoading } = useQuery({ + queryKey: ['subjects'], + queryFn: async () => { + const response = await api.get('/subjects/') + return response.data.results || response.data + }, + }) + + const { data: departments } = useQuery({ + queryKey: ['departments'], + queryFn: async () => { + const response = await api.get('/departments/') + return response.data.results || response.data + }, + }) + + const createMutation = useMutation({ + mutationFn: (data) => api.post('/subjects/', data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['subjects'] }) + setShowModal(false) + }, + }) + + const updateMutation = useMutation({ + mutationFn: ({ id, data }) => api.patch(`/subjects/${id}/`, data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['subjects'] }) + setShowModal(false) + setEditingItem(null) + }, + }) + + const deleteMutation = useMutation({ + mutationFn: (id) => api.delete(`/subjects/${id}/`), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['subjects'] }) + }, + }) + + const handleSubmit = (e) => { + e.preventDefault() + const formData = new FormData(e.currentTarget) + const data = { + code: formData.get('code'), + name: formData.get('name'), + credits: parseInt(formData.get('credits')), + weekly_quota: parseInt(formData.get('weekly_quota')), + is_lab: formData.get('is_lab') === 'on', + department: formData.get('department') || null, + } + if (editingItem) { + updateMutation.mutate({ id: editingItem.id, data }) + } else { + createMutation.mutate(data) + } + } + + if (isLoading) return
Loading...
+ + return ( +
+
+

Subjects

+ +
+ +
+ + + + + + + + + + + + + {subjects?.map((subject) => ( + + + + + + + + + ))} + +
CodeNameCreditsWeekly QuotaTypeActions
{subject.code}{subject.name}{subject.credits}{subject.weekly_quota} + + {subject.is_lab ? 'Lab' : 'Theory'} + + + + +
+
+ + {showModal && ( + { + setShowModal(false) + setEditingItem(null) + }} + > +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ )} +
+ ) +} + +// Sections Tab +function SectionsTab() { + const [showModal, setShowModal] = useState(false) + const [editingItem, setEditingItem] = useState(null) + const queryClient = useQueryClient() + + const { data: sections, isLoading } = useQuery({ + queryKey: ['sections'], + queryFn: async () => { + const response = await api.get('/sections/') + return response.data.results || response.data + }, + }) + + const { data: departments } = useQuery({ + queryKey: ['departments'], + queryFn: async () => { + const response = await api.get('/departments/') + return response.data.results || response.data + }, + }) + + const createMutation = useMutation({ + mutationFn: (data) => api.post('/sections/', data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['sections'] }) + setShowModal(false) + }, + }) + + const updateMutation = useMutation({ + mutationFn: ({ id, data }) => api.patch(`/sections/${id}/`, data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['sections'] }) + setShowModal(false) + setEditingItem(null) + }, + }) + + const deleteMutation = useMutation({ + mutationFn: (id) => api.delete(`/sections/${id}/`), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['sections'] }) + }, + }) + + const handleSubmit = (e) => { + e.preventDefault() + const formData = new FormData(e.currentTarget) + const data = { + name: formData.get('name'), + student_strength: parseInt(formData.get('student_strength')), + department: formData.get('department') || null, + year: formData.get('year') ? parseInt(formData.get('year')) : null, + semester: formData.get('semester') ? parseInt(formData.get('semester')) : null, + } + if (editingItem) { + updateMutation.mutate({ id: editingItem.id, data }) + } else { + createMutation.mutate(data) + } + } + + if (isLoading) return
Loading...
+ + return ( +
+
+

Sections

+ +
+ +
+ + + + + + + + + + + + {sections?.map((section) => ( + + + + + + + + ))} + +
NameStrengthYearSemesterActions
{section.name}{section.student_strength}{section.year || '-'}{section.semester || '-'} + + +
+
+ + {showModal && ( + { + setShowModal(false) + setEditingItem(null) + }} + > +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ )} +
+ ) +} + +// Classrooms Tab +function ClassroomsTab() { + const [showModal, setShowModal] = useState(false) + const [editingItem, setEditingItem] = useState(null) + const queryClient = useQueryClient() + + const { data: classrooms, isLoading } = useQuery({ + queryKey: ['classrooms'], + queryFn: async () => { + const response = await api.get('/classrooms/') + return response.data.results || response.data + }, + }) + + const { data: departments } = useQuery({ + queryKey: ['departments'], + queryFn: async () => { + const response = await api.get('/departments/') + return response.data.results || response.data + }, + }) + + const createMutation = useMutation({ + mutationFn: (data) => api.post('/classrooms/', data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['classrooms'] }) + setShowModal(false) + }, + }) + + const updateMutation = useMutation({ + mutationFn: ({ id, data }) => api.patch(`/classrooms/${id}/`, data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['classrooms'] }) + setShowModal(false) + setEditingItem(null) + }, + }) + + const deleteMutation = useMutation({ + mutationFn: (id) => api.delete(`/classrooms/${id}/`), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['classrooms'] }) + }, + }) + + const handleSubmit = (e) => { + e.preventDefault() + const formData = new FormData(e.currentTarget) + const data = { + name: formData.get('name'), + capacity: parseInt(formData.get('capacity')), + is_lab: formData.get('is_lab') === 'on', + department: formData.get('department') || null, + } + if (editingItem) { + updateMutation.mutate({ id: editingItem.id, data }) + } else { + createMutation.mutate(data) + } + } + + if (isLoading) return
Loading...
+ + return ( +
+
+

Classrooms & Labs

+ +
+ +
+ + + + + + + + + + + {classrooms?.map((classroom) => ( + + + + + + + ))} + +
NameCapacityTypeActions
{classroom.name}{classroom.capacity} + + {classroom.is_lab ? 'Lab' : 'Classroom'} + + + + +
+
+ + {showModal && ( + { + setShowModal(false) + setEditingItem(null) + }} + > +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ )} +
+ ) +} + +// Subject-Teacher Mappings Tab +function MappingsTab() { + const [showModal, setShowModal] = useState(false) + const [editingItem, setEditingItem] = useState(null) + const queryClient = useQueryClient() + + const { data: mappings, isLoading } = useQuery({ + queryKey: ['mappings'], + queryFn: async () => { + const response = await api.get('/subject-teacher-mappings/') + return response.data.results || response.data + }, + }) + + const { data: subjects } = useQuery({ + queryKey: ['subjects'], + queryFn: async () => { + const response = await api.get('/subjects/') + return response.data.results || response.data + }, + }) + + const { data: teachers } = useQuery({ + queryKey: ['teachers'], + queryFn: async () => { + const response = await api.get('/teachers/') + return response.data.results || response.data + }, + }) + + const { data: sections } = useQuery({ + queryKey: ['sections'], + queryFn: async () => { + const response = await api.get('/sections/') + return response.data.results || response.data + }, + }) + + const createMutation = useMutation({ + mutationFn: (data) => api.post('/subject-teacher-mappings/', data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['mappings'] }) + setShowModal(false) + }, + }) + + const deleteMutation = useMutation({ + mutationFn: (id) => api.delete(`/subject-teacher-mappings/${id}/`), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['mappings'] }) + }, + }) + + const handleSubmit = (e) => { + e.preventDefault() + const formData = new FormData(e.currentTarget) + const data = { + subject: formData.get('subject'), + teacher: formData.get('teacher'), + section: formData.get('section') || null, + is_primary: formData.get('is_primary') === 'on', + } + createMutation.mutate(data) + } + + if (isLoading) return
Loading...
+ + return ( +
+
+

Subject-Teacher Mappings

+ +
+ +
+ + + + + + + + + + + + {mappings?.map((mapping) => ( + + + + + + + + ))} + +
SubjectTeacherSectionPrimaryActions
{mapping.subject_code} - {mapping.subject_name}{mapping.teacher_name}{mapping.section_name || 'All'} + {mapping.is_primary ? ( + Yes + ) : ( + No + )} + + +
+
+ + {showModal && ( + { + setShowModal(false) + setEditingItem(null) + }} + > +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ )} +
+ ) +} + +// Teacher Preferences Tab +function PreferencesTab() { + const [showModal, setShowModal] = useState(false) + const [selectedTeacher, setSelectedTeacher] = useState(null) + const queryClient = useQueryClient() + + const { data: teachers } = useQuery({ + queryKey: ['teachers'], + queryFn: async () => { + const response = await api.get('/teachers/') + return response.data.results || response.data + }, + }) + + const { data: preferences, isLoading } = useQuery({ + queryKey: ['preferences', selectedTeacher], + queryFn: async () => { + if (!selectedTeacher) return null + const response = await api.get(`/teacher-preferences/?teacher=${selectedTeacher}`) + return response.data.results?.[0] || response.data?.[0] || null + }, + enabled: !!selectedTeacher, + }) + + const createOrUpdateMutation = useMutation({ + mutationFn: (data) => { + if (preferences) { + return api.patch(`/teacher-preferences/${preferences.id}/`, data) + } else { + return api.post('/teacher-preferences/', { ...data, teacher: selectedTeacher }) + } + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['preferences'] }) + setShowModal(false) + }, + }) + + const timeSlots = [ + { id: 1, label: '9:00 - 9:55' }, + { id: 2, label: '9:55 - 10:50' }, + { id: 3, label: '11:10 - 12:05' }, + { id: 4, label: '12:05 - 1:00' }, + { id: 5, label: '1:20 - 2:15' }, + { id: 6, label: '2:15 - 3:10' }, + { id: 7, label: '3:30 - 4:25' }, + ] + + const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'] + + const handleSubmit = (e) => { + e.preventDefault() + const formData = new FormData(e.currentTarget) + const preferredSlots = [] + const preferredDays = [] + const unavailableSlots = [] + + timeSlots.forEach(slot => { + if (formData.get(`pref_slot_${slot.id}`) === 'on') { + preferredSlots.push(slot.id) + } + if (formData.get(`unavail_slot_${slot.id}`) === 'on') { + unavailableSlots.push(slot.id) + } + }) + + days.forEach(day => { + if (formData.get(`pref_day_${day}`) === 'on') { + preferredDays.push(day) + } + }) + + createOrUpdateMutation.mutate({ + preferred_time_slots: preferredSlots, + preferred_days: preferredDays, + unavailable_slots: unavailableSlots, + }) + } + + return ( +
+
+

Teacher Preferences

+
+ +
+ + +
+ + {selectedTeacher && ( +
+ + + {preferences && ( +
+

Current Preferences

+

Preferred Time Slots: {preferences.preferred_time_slots?.join(', ') || 'None'}

+

Preferred Days: {preferences.preferred_days?.join(', ') || 'None'}

+

Unavailable Slots: {preferences.unavailable_slots?.join(', ') || 'None'}

+
+ )} +
+ )} + + {showModal && selectedTeacher && ( + setShowModal(false)} + > +
+
+ +
+ {timeSlots.map(slot => ( + + ))} +
+
+
+ +
+ {days.map(day => ( + + ))} +
+
+
+ +
+ {timeSlots.map(slot => ( + + ))} +
+
+
+ + +
+
+
+ )} +
+ ) +} diff --git a/frontend/src/pages/college/TimetableDetail.jsx b/frontend/src/pages/college/TimetableDetail.jsx new file mode 100644 index 0000000..237948c --- /dev/null +++ b/frontend/src/pages/college/TimetableDetail.jsx @@ -0,0 +1,203 @@ +import { useParams, useNavigate } from 'react-router-dom' +import { useQuery } from '@tanstack/react-query' +import api from '../../lib/api' +import { ArrowLeft, Download } from 'lucide-react' +import * as XLSX from 'xlsx' + +export default function TimetableDetail() { + const { id } = useParams() + const navigate = useNavigate() + + const { data: timetable, isLoading } = useQuery({ + queryKey: ['timetable', id], + queryFn: async () => { + const response = await api.get(`/timetables/${id}/`) + return response.data + }, + }) + + const exportToExcel = () => { + if (!timetable?.timetable_data) return + + const workbook = XLSX.utils.book_new() + + // Process each week + Object.keys(timetable.timetable_data).forEach((weekKey) => { + const weekData = timetable.timetable_data[weekKey] + + // Create a sheet for each week + const weekSheetData = [] + + // Header row + const days = Object.keys(weekData) + const sections = new Set() + + // Collect all sections + days.forEach(day => { + Object.keys(weekData[day] || {}).forEach(section => sections.add(section)) + }) + + // Create header + const header = ['Day', 'Time Slot', ...Array.from(sections)] + weekSheetData.push(header) + + // Process each day + days.forEach(day => { + const dayData = weekData[day] || {} + const timeSlots = new Set() + + // Collect all time slots for this day + Object.values(dayData).forEach(sectionSchedule => { + if (Array.isArray(sectionSchedule)) { + sectionSchedule.forEach(entry => { + if (entry?.time_slot) timeSlots.add(entry.time_slot) + }) + } + }) + + // Create rows for each time slot + Array.from(timeSlots).sort().forEach(timeSlot => { + const row = [day, timeSlot] + + Array.from(sections).forEach(section => { + const sectionSchedule = dayData[section] || [] + const entry = Array.isArray(sectionSchedule) + ? sectionSchedule.find(e => e?.time_slot === timeSlot) + : null + + if (entry) { + const cellValue = `${entry.subject_id || ''}\n${entry.teacher_id || ''}\n${entry.classroom_id || ''}` + row.push(cellValue) + } else { + row.push('') + } + }) + + weekSheetData.push(row) + }) + }) + + const worksheet = XLSX.utils.aoa_to_sheet(weekSheetData) + XLSX.utils.book_append_sheet(workbook, worksheet, weekKey.substring(0, 31)) // Excel sheet name limit + }) + + // Download + const fileName = `${timetable.name || 'timetable'}_${timetable.academic_year || 'unknown'}.xlsx` + XLSX.writeFile(workbook, fileName) + } + + if (isLoading) return
Loading...
+ if (!timetable) return
Timetable not found
+ + const timetableData = timetable.timetable_data || {} + + return ( +
+
+
+ +
+

{timetable.name}

+

+ {timetable.academic_year} - {timetable.semester} +

+
+
+ +
+ +
+ {Object.keys(timetableData).map((weekKey) => { + const weekData = timetableData[weekKey] + const days = Object.keys(weekData || {}) + const sections = new Set() + + days.forEach(day => { + const dayData = weekData[day] || {} + Object.keys(dayData).forEach(section => sections.add(section)) + }) + + return ( +
+
+

{weekKey}

+
+ +
+ + + + + + {Array.from(sections).map(section => ( + + ))} + + + + {days.map(day => { + const dayData = weekData[day] || {} + const timeSlots = new Set() + + Object.values(dayData).forEach(sectionSchedule => { + if (Array.isArray(sectionSchedule)) { + sectionSchedule.forEach(entry => { + if (entry?.time_slot) timeSlots.add(entry.time_slot) + }) + } + }) + + return Array.from(timeSlots).sort().map((timeSlot, idx) => ( + + {idx === 0 && ( + + )} + + {Array.from(sections).map(section => { + const sectionSchedule = dayData[section] || [] + const entry = Array.isArray(sectionSchedule) + ? sectionSchedule.find(e => e?.time_slot === timeSlot) + : null + + return ( + + ) + })} + + )) + })} + +
DayTime Slot + Section {section} +
+ {day} + {timeSlot} + {entry ? ( +
+
{entry.subject_id || '-'}
+
{entry.teacher_id || '-'}
+
{entry.classroom_id || '-'}
+
+ ) : ( + - + )} +
+
+
+ ) + })} +
+
+ ) +} diff --git a/frontend/src/pages/college/Timetables.jsx b/frontend/src/pages/college/Timetables.jsx new file mode 100644 index 0000000..dc47c89 --- /dev/null +++ b/frontend/src/pages/college/Timetables.jsx @@ -0,0 +1,118 @@ +import { useQuery } from '@tanstack/react-query' +import { useNavigate } from 'react-router-dom' +import api from '../../lib/api' +import { Eye, Download } from 'lucide-react' +import * as XLSX from 'xlsx' + +export default function Timetables() { + const navigate = useNavigate() + + const { data: timetables, isLoading } = useQuery({ + queryKey: ['timetables'], + queryFn: async () => { + const response = await api.get('/timetables/') + return response.data.results || response.data + }, + }) + + const exportToExcel = (timetable) => { + if (!timetable?.timetable_data) return + + const workbook = XLSX.utils.book_new() + const weekData = timetable.timetable_data[Object.keys(timetable.timetable_data)[0]] || {} + + const days = Object.keys(weekData) + const sections = new Set() + days.forEach(day => { + Object.keys(weekData[day] || {}).forEach(section => sections.add(section)) + }) + + const sheetData = [['Day', 'Time Slot', ...Array.from(sections)]] + + days.forEach(day => { + const dayData = weekData[day] || {} + const timeSlots = new Set() + + Object.values(dayData).forEach(sectionSchedule => { + if (Array.isArray(sectionSchedule)) { + sectionSchedule.forEach(entry => { + if (entry?.time_slot) timeSlots.add(entry.time_slot) + }) + } + }) + + Array.from(timeSlots).sort().forEach(timeSlot => { + const row = [day, timeSlot] + Array.from(sections).forEach(section => { + const sectionSchedule = dayData[section] || [] + const entry = Array.isArray(sectionSchedule) + ? sectionSchedule.find(e => e?.time_slot === timeSlot) + : null + row.push(entry ? `${entry.subject_id || ''} (${entry.teacher_id || ''}) - ${entry.classroom_id || ''}` : '') + }) + sheetData.push(row) + }) + }) + + const worksheet = XLSX.utils.aoa_to_sheet(sheetData) + XLSX.utils.book_append_sheet(workbook, worksheet, 'Timetable') + + const fileName = `${timetable.name || 'timetable'}_${timetable.academic_year || 'unknown'}.xlsx` + XLSX.writeFile(workbook, fileName) + } + + if (isLoading) return
Loading...
+ + return ( +
+

Timetables

+
+ {timetables?.map((timetable) => ( +
+

{timetable.name}

+

+ {timetable.academic_year} - {timetable.semester} +

+
+ + {timetable.status} + + {timetable.fitness_score && ( + + Score: {timetable.fitness_score.toFixed(2)} + + )} +
+
+ + +
+
+ ))} + {(!timetables || timetables.length === 0) && ( +
+ No timetables found. Generate your first timetable! +
+ )} +
+
+ ) +} diff --git a/frontend/src/pages/master-admin/Colleges.jsx b/frontend/src/pages/master-admin/Colleges.jsx new file mode 100644 index 0000000..de58104 --- /dev/null +++ b/frontend/src/pages/master-admin/Colleges.jsx @@ -0,0 +1,218 @@ +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' +import { useState } from 'react' +import api from '../../lib/api' +import { Plus, Edit, Trash2 } from 'lucide-react' + +export default function Colleges() { + const [showModal, setShowModal] = useState(false) + const [editingCollege, setEditingCollege] = useState(null) + const queryClient = useQueryClient() + + const { data: colleges, isLoading } = useQuery({ + queryKey: ['colleges'], + queryFn: async () => { + const response = await api.get('/colleges/') + return response.data.results || response.data + }, + }) + + const createMutation = useMutation({ + mutationFn: (data) => api.post('/colleges/', data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['colleges'] }) + setShowModal(false) + }, + }) + + const updateMutation = useMutation({ + mutationFn: ({ id, data }) => + api.patch(`/colleges/${id}/`, data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['colleges'] }) + setShowModal(false) + setEditingCollege(null) + }, + }) + + const deleteMutation = useMutation({ + mutationFn: (id) => api.delete(`/colleges/${id}/`), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['colleges'] }) + }, + }) + + const handleSubmit = (e) => { + e.preventDefault() + const formData = new FormData(e.currentTarget) + const data = { + name: formData.get('name'), + code: formData.get('code'), + subdomain: formData.get('subdomain'), + email: formData.get('email'), + phone: formData.get('phone'), + address: formData.get('address'), + } + + if (editingCollege) { + updateMutation.mutate({ id: editingCollege.id, data }) + } else { + createMutation.mutate(data) + } + } + + if (isLoading) return
Loading...
+ + return ( +
+
+

Colleges

+ +
+ +
+ + + + + + + + + + + + {colleges?.map((college) => ( + + + + + + + + ))} + +
+ Name + + Code + + Subdomain + + Status + + Actions +
+ {college.name} + + {college.code} + + {college.subdomain || '-'} + + + {college.is_active ? 'Active' : 'Inactive'} + + + + +
+
+ + {showModal && ( +
+
+

+ {editingCollege ? 'Edit College' : 'Add College'} +

+
+ + + + + + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+ + + +
+

Result

+
Fill the form and click "Generate Timetable".
+
+
+ + + + \ No newline at end of file diff --git a/or-tool.py b/or-tool.py new file mode 100644 index 0000000..7b07413 --- /dev/null +++ b/or-tool.py @@ -0,0 +1,151 @@ +import random + +from ortools.sat.python import cp_model + +days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] +periods = [ + "9:00 - 9:55", + "9:55 - 10:50", + "11:10 - 12:05", + "12:05 - 1:00", + "1:20 - 2:15", + "2:15 - 3:10", + "3:30 - 4:25", +] +sections = ["A", "B", "C", "D"] +rooms = [ + ("R2", 100, "regular"), + ("R3", 100, "regular"), + ("R4", 100, "regular"), + ("R5", 100, "regular"), + ("L1", 30, "lab"), + ("L2", 30, "lab"), + ("L3", 30, "lab"), + ("L4", 30, "lab"), + ("L5", 30, "lab"), + ("L6", 30, "lab"), +] +# Sample subjects and teachers. +subjects = [ + "TCS-509", + "TCS-503", + "TCS-502", + "TCS-531", + "TMA-502", + "PCS-503", + "PCS-506", + "CSP-501", + "XCS-501", + "SCS-501", + "PMA-502", + "Placement_Class", + "Library", +] +teachers = [ + "HP18", + "AC05", + "JM12", + "RS11", + "DT20", + "AB17", + "DP07", + "AA04", + "AB01", + "SS03", + "SP06", + "NJ13", + "PA21", + "AK23", + "RD09", + "PM14", + "AK26", + "AA15", + "None", +] + +# For demonstration we create a list of events. +# Each event has a fixed day, section, period, subject, teacher and group. +# Group 'all' (≈60 students) requires a regular room (capacity ≥60) +# Numeric group (e.g. 1 or 2, ≈30 students) requires a lab (capacity ≥30) +events = [] +for d in days: + for s in sections: + for p in range(len(periods)): + subj = random.choice(subjects) + teach = random.choice(teachers) + group = random.choice(["all", 1, 2]) + events.append( + { + "day": d, + "section": s, + "period": p, + "subject": subj, + "teacher": teach, + "group": group, + } + ) + +model = cp_model.CpModel() +vars_rooms = [] +for i, ev in enumerate(events): + allowed = [] + for r_idx, (rname, cap, rtype) in enumerate(rooms): + if ev["group"] == "all": + if rtype == "regular" and cap >= 60: + allowed.append(r_idx) + else: + if rtype == "lab" and cap >= 30: + allowed.append(r_idx) + if not allowed: + allowed = list(range(len(rooms))) + v = model.NewIntVarFromDomain(cp_model.Domain.FromValues(allowed), f"room_{i}") + vars_rooms.append(v) + +# No two events in the same day and period can share a room. +for d in days: + for p in range(len(periods)): + idxs = [ + i for i, ev in enumerate(events) if ev["day"] == d and ev["period"] == p + ] + if len(idxs) > 1: + model.AddAllDifferent([vars_rooms[i] for i in idxs]) + +# For teacher conflicts (ignoring 'None'). +for d in days: + for p in range(len(periods)): + teach_to_ids = {} + for i, ev in enumerate(events): + if ev["day"] == d and ev["period"] == p and ev["teacher"] != "None": + teach_to_ids.setdefault(ev["teacher"], []).append(i) + for ids in teach_to_ids.values(): + if len(ids) > 1: + # If a teacher is scheduled twice at the same time, the model becomes infeasible. + # In practice, input data should avoid such conflicts. + model.AddAllDifferent([vars_rooms[i] for i in ids]) + +solver = cp_model.CpSolver() +status = solver.Solve(model) + +schedule = {} +if status in (cp_model.FEASIBLE, cp_model.OPTIMAL): + for ev, var in zip(events, vars_rooms): + room_id = rooms[solver.Value(var)][0] + d, s, p = ev["day"], ev["section"], ev["period"] + schedule.setdefault(d, {}).setdefault(s, []).append( + { + "classroom_id": room_id, + "group": ev["group"], + "subject_id": ev["subject"], + "teacher_id": ev["teacher"], + "time_slot": periods[p], + } + ) + for d in schedule: + for s in schedule[d]: + schedule[d][s].sort(key=lambda x: periods.index(x["time_slot"])) + from icecream import ic + + ic(schedule) + +else: + print("No solution found.") diff --git a/resources.html b/resources.html new file mode 100644 index 0000000..18a0e01 --- /dev/null +++ b/resources.html @@ -0,0 +1,232 @@ + + + + + + Placement Resources, GEHU + + + + +
+

Placement Resources, GEHU

+ +
+ +
+

Data Structures and Algorithms

+

Hello! I am a placement teacher, and this website is designed to help you prepare for placements. Explore the resources, check placement dates, and feel free to reach out if you have any questions.

+
+ +
+

Study Resources

+ +
+ +
+

Upcoming Placement Dates

+ + + + + + + + + + + + + + + + + + + + + +
CompanyDate
Company AOctober 15, 2023
Company BNovember 1, 2023
Company CNovember 15, 2023
+
+ +
+

Contact Me

+

If you have any questions, feel free to reach out:

+ +
+
+

Data Structures and Algorithms

+

Hello! I am a placement teacher, and this website is designed to help you prepare for placements. Explore the resources, check placement dates, and feel free to reach out if you have any questions.

+
+
+

Data Structures and Algorithms

+

Hello! I am a placement teacher, and this website is designed to help you prepare for placements. Explore the resources, check placement dates, and feel free to reach out if you have any questions.

+
+
+

Data Structures and Algorithms

+

Hello! I am a placement teacher, and this website is designed to help you prepare for placements. Explore the resources, check placement dates, and feel free to reach out if you have any questions.

+
+
+

Data Structures and Algorithms

+

Hello! I am a placement teacher, and this website is designed to help you prepare for placements. Explore the resources, check placement dates, and feel free to reach out if you have any questions.

+
+
+

Data Structures and Algorithms

+

Hello! I am a placement teacher, and this website is designed to help you prepare for placements. Explore the resources, check placement dates, and feel free to reach out if you have any questions.

+
+
+

Data Structures and Algorithms

+

Hello! I am a placement teacher, and this website is designed to help you prepare for placements. Explore the resources, check placement dates, and feel free to reach out if you have any questions.

+
+
+

© 2025 All rights reserved. GEHU, Bhimtal

+
+ + \ No newline at end of file diff --git a/setup.py b/setup.py index 7ea9ea4..c5c0b99 100644 --- a/setup.py +++ b/setup.py @@ -2,16 +2,17 @@ setup( name="TimeTableLib", - packages=find_packages(include=["managers", "managers.common"]), + packages=find_packages(include=["GA", "Samples", "Constants"]), version="v0.1.0", description="This Library generates timetable and is a plug and play solution for any python based backend service.", - long_description=open("README.md").read(), + long_description=open("README.md", encoding="utf-8").read(), authors=["Graphic Era Hill University, Bhimtal"], license="MIT", install_requires=[ "psycopg2-binary==2.9.9", "pytest==7.4.2", - "ortools==9.11.4210" + "ortools==9.11.4210", + "icecream==2.1.3", ], # noqa: E501 setup_requires=["pytest-runner"], tests_require=["pytest==7.4.2"], diff --git a/test-suite/ulits_test.py b/test-suite/ulits_test.py deleted file mode 100644 index 27de49a..0000000 --- a/test-suite/ulits_test.py +++ /dev/null @@ -1,3 +0,0 @@ -from Constants.TimeIntervals import TimeIntervalConstant as ti - -print(ti.get_slot_number("09:00", "09:55")) # Output: 1 diff --git a/test-suite/ReadMe.md b/test_suite/ReadMe.md similarity index 100% rename from test-suite/ReadMe.md rename to test_suite/ReadMe.md diff --git a/test_suite/test_crossover.py b/test_suite/test_crossover.py new file mode 100644 index 0000000..2bfc568 --- /dev/null +++ b/test_suite/test_crossover.py @@ -0,0 +1,750 @@ +# CROSSOVER + +# 1. def create_chromosome(self): + +import random +import unittest + + +# TimetableScheduler Class +class TimetableScheduler: + def __init__(self): + self.sections = ["A", "B", "C", "D"] + self.time_slots = [1, 2, 3, 4, 5, 6, 7] # Time slots per day + self.days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"] + self.subject_teacher_map = { + "TCS-531": ["AB01", "PK02"], + "TCS-502": ["SS03", "AA04", "AC05"], + "TCS-503": ["SP06", "DP07", "AC05"], + "PCS-506": ["AD08", "RD09"], + "TMA-502": ["BJ10", "RS11", "JM12", "NJ13"], + "PMA-502": ["PM14", "AD08", "AA15"], + "TCS-509": ["SJ16", "AB17", "HP18", "SG19"], + "XCS-501": ["DT20", "PA21", "NB22"], + "CSP-501": ["AK23"], + "SCS-501": ["AP24"], + } + self.classrooms = ["R1", "R2", "R3", "R4", "R5"] + self.room_capacity = {"R1": 200, "R2": 230, "R3": 240, "R4": 250, "R5": 250} + self.section_strength = {"A": 200, "B": 200, "C": 200, "D": 100} + + def create_chromosome(self): + schedule = {} + teacher_schedule = {slot: set() for slot in self.time_slots} + room_schedule = {slot: {} for slot in self.time_slots} + + for day in self.days: + schedule[day] = {} + for section in self.sections: + schedule[day][section] = [] + used_time_slots = set() + + while len(schedule[day][section]) < 7: + subject = random.choice(list(self.subject_teacher_map.keys())) + teacher = random.choice(self.subject_teacher_map[subject]) + classroom = random.choice(self.classrooms) + + # Find an available time slot + available_slots = [ + slot for slot in self.time_slots if slot not in used_time_slots + ] + if not available_slots: + break + + time_slot = random.choice(available_slots) + + # Check for conflicts + if teacher not in teacher_schedule[ + time_slot + ] and section not in room_schedule[time_slot].get(classroom, []): + entry = { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": classroom, + "time_slot": time_slot, + } + schedule[day][section].append(entry) + used_time_slots.add(time_slot) + + # Update schedules to avoid future conflicts + teacher_schedule[time_slot].add(teacher) + if classroom not in room_schedule[time_slot]: + room_schedule[time_slot][classroom] = [] + room_schedule[time_slot][classroom].append(section) + + # Ensure the section has all 7 slots for the day + if len(schedule[day][section]) < 7: + raise RuntimeError( + f"Failed to generate a complete schedule for section {section} on {day}." + ) + + return schedule + + +# Unit Test Class for TimetableScheduler +class TestTimetableScheduler(unittest.TestCase): + def setUp(self): + self.scheduler = TimetableScheduler() + + def test_schedule_for_all_days_and_sections(self): + schedule = self.scheduler.create_chromosome() + + # Check every day has all sections with 7 time slots + for day in self.scheduler.days: + self.assertIn(day, schedule) + for section in self.scheduler.sections: + self.assertIn(section, schedule[day]) + self.assertEqual(len(schedule[day][section]), 7) + + def test_conflict_free_teacher_allocation(self): + schedule = self.scheduler.create_chromosome() + + # Check for teacher conflicts + for day in self.scheduler.days: + teacher_schedule = {slot: set() for slot in self.scheduler.time_slots} + for section in self.scheduler.sections: + for entry in schedule[day][section]: + teacher = entry["teacher_id"] + time_slot = entry["time_slot"] + self.assertNotIn(teacher, teacher_schedule[time_slot]) + teacher_schedule[time_slot].add(teacher) + + def test_room_capacity(self): + schedule = self.scheduler.create_chromosome() + + # Ensure room capacity meets section strength + for day in self.scheduler.days: + for section in self.scheduler.sections: + for entry in schedule[day][section]: + classroom = entry["classroom_id"] + self.assertGreaterEqual( + self.scheduler.room_capacity[classroom], + self.scheduler.section_strength[section], + ) + + def test_time_slots_coverage(self): + schedule = self.scheduler.create_chromosome() + + # Check if all time slots are covered per section per day + for day in self.scheduler.days: + for section in self.scheduler.sections: + time_slots = [entry["time_slot"] for entry in schedule[day][section]] + self.assertEqual(len(set(time_slots)), 7) # Ensure exactly 7 slots + + +if __name__ == "__main__": + unittest.main() + +# 2. def create_multiple_chromosomes(self, num_chromosomes): + +import random +import time +import unittest + + +class TimetableScheduler: + def __init__(self, subjects, rooms, days, sections, max_attempts=10): + self.subjects = subjects # List of subject codes + self.rooms = rooms # List of available rooms + self.days = days # List of days in the week + self.sections = sections # List of sections + self.max_attempts = max_attempts # Max attempts to find a valid slot + + self.schedule = {} # Schedule dictionary to store the timetable + + def generate_random_schedule(self): + """Generate a random schedule for each subject and section.""" + timetable = {} + for subject in self.subjects: + for section in self.sections: + attempts = 0 + while attempts < self.max_attempts: + # Randomly select a day, room, and time slot + day = random.choice(self.days) + room = random.choice(self.rooms) + time_slot = f"{day}_{room}" + + # Check if the time_slot is available (no conflicts) + if self.is_slot_available(subject, section, time_slot): + timetable[subject] = (section, day, room, time_slot) + break + else: + attempts += 1 + if attempts == self.max_attempts: + raise RuntimeError( + f"Failed to assign a valid slot for {subject} on {day} for {section} after {attempts} attempts." + ) + return timetable + + def is_slot_available(self, subject, section, time_slot): + """Check if a slot is available by ensuring no conflicts.""" + # Check if any other subject has been scheduled in the same time slot + for key, value in self.schedule.items(): + if value[3] == time_slot: # Slot conflict + return False + return True + + def create_chromosome(self): + """Create a chromosome that represents a valid timetable.""" + attempts = 0 + while attempts < self.max_attempts: + try: + timetable = self.generate_random_schedule() + self.schedule = ( + timetable # Assign the generated schedule to the global timetable + ) + return timetable + except RuntimeError as e: + print(f"Error: {e}") + attempts += 1 + if attempts == self.max_attempts: + raise RuntimeError( + "Failed to generate a valid timetable after multiple attempts." + ) + # Reset and retry + self.schedule = {} + + return None + + def create_multiple_chromosomes(self, num_chromosomes): + """Create multiple chromosomes (timetables).""" + chromosomes = [] + for _ in range(num_chromosomes): + chromosome = self.create_chromosome() + if chromosome: + chromosomes.append(chromosome) + else: + print("Failed to create a valid chromosome.") + return chromosomes + + +class TestTimetableScheduler(unittest.TestCase): + def setUp(self): + """Setup the test case environment.""" + self.subjects = ["PCS-506", "XCS-501", "TCS-502", "CSP-501", "TCS-531"] + self.rooms = ["Room 101", "Room 102", "Room 103"] + self.days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"] + self.sections = ["A", "B", "C"] + + self.scheduler = TimetableScheduler( + self.subjects, self.rooms, self.days, self.sections + ) + + def test_chromosome_structure(self): + """Test the structure of each chromosome.""" + num_chromosomes = 3 + start_time = time.time() + chromosomes = self.scheduler.create_multiple_chromosomes(num_chromosomes) + end_time = time.time() + + self.assertEqual(len(chromosomes), num_chromosomes) + print(f"Ran {num_chromosomes} tests in {end_time - start_time:.4f} seconds") + + for idx, chromosome in enumerate(chromosomes): + print(f"Chromosome {idx+1}:") + for subject, (section, day, room, time_slot) in chromosome.items(): + print(f"{subject} - {section} - {day} - {room} - {time_slot}") + print() + + def test_conflict_free_chromosomes(self): + """Test that chromosomes are conflict-free.""" + num_chromosomes = 3 + start_time = time.time() + chromosomes = self.scheduler.create_multiple_chromosomes(num_chromosomes) + end_time = time.time() + + self.assertEqual(len(chromosomes), num_chromosomes) + print(f"Ran {num_chromosomes} tests in {end_time - start_time:.4f} seconds") + + for idx, chromosome in enumerate(chromosomes): + for subject, (section, day, room, time_slot) in chromosome.items(): + # Check for conflicts (same time slot for different subjects) + for other_subject, other_values in chromosome.items(): + if subject != other_subject and time_slot == other_values[3]: + self.fail( + f"Conflict detected: {subject} and {other_subject} in the same time slot: {time_slot}" + ) + + def test_create_multiple_chromosomes_count(self): + """Test if the correct number of chromosomes is generated.""" + num_chromosomes = 5 + start_time = time.time() + chromosomes = self.scheduler.create_multiple_chromosomes(num_chromosomes) + end_time = time.time() + + self.assertEqual(len(chromosomes), num_chromosomes) + print(f"Ran {num_chromosomes} tests in {end_time - start_time:.4f} seconds") + + def test_room_capacity_in_multiple_chromosomes(self): + """Test room capacity constraint across multiple chromosomes.""" + num_chromosomes = 3 + start_time = time.time() + chromosomes = self.scheduler.create_multiple_chromosomes(num_chromosomes) + end_time = time.time() + + self.assertEqual(len(chromosomes), num_chromosomes) + print(f"Ran {num_chromosomes} tests in {end_time - start_time:.4f} seconds") + + # Test room capacity constraints for each timetable (e.g., number of rooms used) + for idx, chromosome in enumerate(chromosomes): + room_usage = {} + for subject, (section, day, room, time_slot) in chromosome.items(): + room_usage[room] = room_usage.get(room, 0) + 1 + print(f"Chromosome {idx+1} Room Usage: {room_usage}") + + +# Run the tests +if __name__ == "__main__": + unittest.main() + +# 3. def crossover(self, parent1, parent2): + +import random +import unittest + + +class TimetableScheduler: + def __init__(self, subjects, rooms, days, sections): + self.subjects = subjects # List of subject codes + self.rooms = rooms # List of available rooms + self.days = days # List of days in the week + self.sections = sections # List of sections + + def crossover(self, parent1, parent2): + """Perform single-point crossover on two parent chromosomes.""" + offspring1 = {} + offspring2 = {} + + days = self.days + crossover_point = random.randint(1, len(days) - 1) + + for i, day in enumerate(days): + if i < crossover_point: + offspring1[day] = parent1[day] + offspring2[day] = parent2[day] + else: + offspring1[day] = parent2[day] + offspring2[day] = parent1[day] + + return offspring1, offspring2 + + +# Unit test for the crossover function +class TestCrossoverMethod(unittest.TestCase): + def setUp(self): + # Example data to simulate chromosomes for testing + self.subjects = ["PCS-506", "XCS-501", "TCS-502", "CSP-501", "TCS-531"] + self.rooms = ["Room 101", "Room 102", "Room 103"] + self.days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"] + self.sections = ["A", "B", "C"] + + # Example chromosomes (parents) + self.parent1 = { + "Monday": "PCS-506", + "Tuesday": "XCS-501", + "Wednesday": "TCS-502", + "Thursday": "CSP-501", + "Friday": "TCS-531", + } + self.parent2 = { + "Monday": "TCS-502", + "Tuesday": "PCS-506", + "Wednesday": "CSP-501", + "Thursday": "TCS-531", + "Friday": "XCS-501", + } + + # Create an instance of the TimetableScheduler + self.scheduler = TimetableScheduler( + self.subjects, self.rooms, self.days, self.sections + ) + + def test_crossover_correct_number_of_offspring(self): + """Test if crossover produces exactly two offspring.""" + offspring1, offspring2 = self.scheduler.crossover(self.parent1, self.parent2) + self.assertEqual(len(offspring1), len(self.days)) + self.assertEqual(len(offspring2), len(self.days)) + + def test_crossover_correct_assignment(self): + """Test if crossover correctly assigns the parts from parents.""" + offspring1, offspring2 = self.scheduler.crossover(self.parent1, self.parent2) + + # Get the crossover point from the offspring's first day in the timetable + crossover_point = next( + i + for i, day in enumerate(self.days) + if offspring1[self.days[i]] != self.parent1[self.days[i]] + ) + + # Check the assignment from parent1 and parent2 in the offspring + for i in range(crossover_point): + self.assertEqual(offspring1[self.days[i]], self.parent1[self.days[i]]) + self.assertEqual(offspring2[self.days[i]], self.parent2[self.days[i]]) + + for i in range(crossover_point, len(self.days)): + self.assertEqual(offspring1[self.days[i]], self.parent2[self.days[i]]) + self.assertEqual(offspring2[self.days[i]], self.parent1[self.days[i]]) + + def test_crossover_randomness(self): + """Test if the crossover point is truly random by checking different results.""" + offspring1_a, offspring2_a = self.scheduler.crossover( + self.parent1, self.parent2 + ) + offspring1_b, offspring2_b = self.scheduler.crossover( + self.parent1, self.parent2 + ) + + # Check that the offspring are not the same (i.e., crossover point was different) + self.assertNotEqual(offspring1_a, offspring1_b) + self.assertNotEqual(offspring2_a, offspring2_b) + + def test_crossover_empty_parent(self): + """Test if crossover handles empty parents gracefully.""" + empty_parent = {} + with self.assertRaises(KeyError): + self.scheduler.crossover(empty_parent, self.parent2) + + with self.assertRaises(KeyError): + self.scheduler.crossover(self.parent1, empty_parent) + + +if __name__ == "__main__": + unittest.main() + +# 4. def mutate_timetable(self, chromosome, mutation_rate=0.7): + +import random +import unittest + + +class TimetableScheduler: + def __init__(self, classrooms, sections, time_slots): + self.classrooms = classrooms # List of classrooms available for assignment + self.sections = sections # List of sections + self.time_slots = time_slots # List of time slots available for scheduling + + def mutate_timetable(self, chromosome, mutation_rate=0.7): + """Applies mutation to a timetable chromosome.""" + classrooms = self.classrooms + sections = self.sections + mutated_chromosome = chromosome.copy() + + for day, section_schedule in mutated_chromosome.items(): + for section, entries in section_schedule.items(): + for entry in entries: + if random.random() < mutation_rate: + entry["time_slot"] = random.choice(self.time_slots) + entry["classroom_id"] = random.choice(classrooms) + entry["section"] = random.choice(sections) + + return mutated_chromosome + + +class TestMutateTimetable(unittest.TestCase): + def setUp(self): + # Example data to simulate a timetable chromosome + self.classrooms = ["Room 101", "Room 102", "Room 103"] + self.sections = ["A", "B", "C"] + self.time_slots = ["9:00 AM", "10:00 AM", "11:00 AM", "1:00 PM", "2:00 PM"] + + # Example timetable (chromosome) + self.chromosome = { + "Monday": { + "A": [ + {"time_slot": "9:00 AM", "classroom_id": "Room 101", "section": "A"} + ], + "B": [ + { + "time_slot": "10:00 AM", + "classroom_id": "Room 102", + "section": "B", + } + ], + "C": [ + { + "time_slot": "11:00 AM", + "classroom_id": "Room 103", + "section": "C", + } + ], + }, + "Tuesday": { + "A": [ + {"time_slot": "9:00 AM", "classroom_id": "Room 101", "section": "A"} + ], + "B": [ + { + "time_slot": "10:00 AM", + "classroom_id": "Room 102", + "section": "B", + } + ], + "C": [ + { + "time_slot": "11:00 AM", + "classroom_id": "Room 103", + "section": "C", + } + ], + }, + } + + # Create an instance of the TimetableScheduler + self.scheduler = TimetableScheduler( + self.classrooms, self.sections, self.time_slots + ) + + def test_mutation_occurrence(self): + """Test if the mutation occurs with the expected frequency.""" + mutation_rate = 0.7 # Mutation rate is set to 70% + + # Run multiple mutations to get more stable results + num_runs = 10 # You can try more runs for higher accuracy + mutation_count = 0 + total_entries = sum( + len(entries) + for day in self.chromosome.values() + for section, entries in day.items() + ) + + for _ in range(num_runs): + mutated_chromosome = self.scheduler.mutate_timetable( + self.chromosome, mutation_rate + ) + + # Count how many entries were mutated + mutated_count = 0 + for day, section_schedule in mutated_chromosome.items(): + for section, entries in section_schedule.items(): + for entry in entries: + # Check if the mutation applied (i.e., values changed) + if ( + entry["time_slot"] != "9:00 AM" + or entry["classroom_id"] != "Room 101" + or entry["section"] != "A" + ): + mutated_count += 1 + + mutation_percentage = mutated_count / total_entries + mutation_count += mutation_percentage # Accumulate mutation percentages + + # Calculate average mutation rate across runs + average_mutation_percentage = mutation_count / num_runs + print( + f"Average Mutation Rate: {average_mutation_percentage}" + ) # To check the mutation rate in the output + + # Assert that mutation occurs in the expected range + self.assertGreaterEqual( + average_mutation_percentage, 0.5 + ) # At least 50% mutation + self.assertLessEqual( + average_mutation_percentage, 1.0 + ) # No more than 100% mutation + + def test_mutate_timetable(self): + """Test if mutation correctly applies changes to the timetable.""" + mutated_chromosome = self.scheduler.mutate_timetable( + self.chromosome, mutation_rate=1.0 + ) + + # Check if mutation has applied changes + for day, section_schedule in mutated_chromosome.items(): + for section, entries in section_schedule.items(): + for entry in entries: + # Check if time_slot, classroom_id, and section have been mutated + self.assertIn(entry["time_slot"], self.time_slots) + self.assertIn(entry["classroom_id"], self.classrooms) + self.assertIn(entry["section"], self.sections) + + def test_mutation_validity(self): + """Test if the mutated timetable remains valid.""" + mutated_chromosome = self.scheduler.mutate_timetable( + self.chromosome, mutation_rate=1.0 + ) + + # Validate that the mutation results in valid values (time_slot, classroom_id, section) + for day, section_schedule in mutated_chromosome.items(): + for section, entries in section_schedule.items(): + for entry in entries: + # Ensure the mutated values are within the expected lists + self.assertIn(entry["time_slot"], self.time_slots) + self.assertIn(entry["classroom_id"], self.classrooms) + self.assertIn(entry["section"], self.sections) + + def test_mutate_empty_chromosome(self): + """Test mutation with an empty chromosome.""" + empty_chromosome = {} + mutated_chromosome = self.scheduler.mutate_timetable( + empty_chromosome, mutation_rate=1.0 + ) + self.assertEqual(mutated_chromosome, empty_chromosome) # Should remain empty + + +if __name__ == "__main__": + unittest.main() + +# 5.def print_chromosomes(self, chromosomes): + +import unittest +from io import StringIO +from unittest.mock import patch + + +class TimetableScheduler: + def __init__(self, classrooms, sections, teachers, subjects, time_slots): + self.classrooms = classrooms # List of classrooms available for assignment + self.sections = sections # List of sections + self.teachers = teachers # List of teacher IDs + self.subjects = subjects # List of subject IDs + self.time_slots = time_slots # List of time slots available for scheduling + + def print_chromosomes(self, chromosomes): + for idx, chromosome in enumerate(chromosomes, 1): + print(f"\nChromosome {idx}:") + for day, sections in chromosome.items(): + print(f" {day}:") + for section, schedule in sections.items(): + print(f" Section {section}:") + for entry in schedule: + print(f" Time Slot: {entry['time_slot']}") + print(f" Teacher ID: {entry['teacher_id']}") + print(f" Subject ID: {entry['subject_id']}") + print(f" Classroom ID: {entry['classroom_id']}") + print() + + +class TestTimetableScheduler(unittest.TestCase): + def setUp(self): + # Example data to simulate a timetable chromosome + self.classrooms = ["Room 101", "Room 102", "Room 103"] + self.sections = ["A", "B", "C"] + self.teachers = ["T1", "T2", "T3"] + self.subjects = ["S1", "S2", "S3"] + self.time_slots = ["9:00 AM", "10:00 AM", "11:00 AM", "1:00 PM", "2:00 PM"] + + # Example timetable chromosomes + self.chromosomes = [ + { + "Monday": { + "A": [ + { + "time_slot": "9:00 AM", + "teacher_id": "T1", + "subject_id": "S1", + "classroom_id": "Room 101", + } + ], + "B": [ + { + "time_slot": "10:00 AM", + "teacher_id": "T2", + "subject_id": "S2", + "classroom_id": "Room 102", + } + ], + "C": [ + { + "time_slot": "11:00 AM", + "teacher_id": "T3", + "subject_id": "S3", + "classroom_id": "Room 103", + } + ], + }, + "Tuesday": { + "A": [ + { + "time_slot": "9:00 AM", + "teacher_id": "T1", + "subject_id": "S1", + "classroom_id": "Room 101", + } + ], + "B": [ + { + "time_slot": "10:00 AM", + "teacher_id": "T2", + "subject_id": "S2", + "classroom_id": "Room 102", + } + ], + "C": [ + { + "time_slot": "11:00 AM", + "teacher_id": "T3", + "subject_id": "S3", + "classroom_id": "Room 103", + } + ], + }, + } + ] + + # Create an instance of the TimetableScheduler + self.scheduler = TimetableScheduler( + self.classrooms, + self.sections, + self.teachers, + self.subjects, + self.time_slots, + ) + + @patch("sys.stdout", new_callable=StringIO) + def test_print_chromosomes(self, mock_stdout): + """Test if print_chromosomes correctly prints the chromosome information.""" + self.scheduler.print_chromosomes(self.chromosomes) + + # Get the printed output + output = mock_stdout.getvalue() + + # Expected output for the given chromosomes + expected_output = """ +Chromosome 1: + Monday: + Section A: + Time Slot: 9:00 AM + Teacher ID: T1 + Subject ID: S1 + Classroom ID: Room 101 + + Section B: + Time Slot: 10:00 AM + Teacher ID: T2 + Subject ID: S2 + Classroom ID: Room 102 + + Section C: + Time Slot: 11:00 AM + Teacher ID: T3 + Subject ID: S3 + Classroom ID: Room 103 + + Tuesday: + Section A: + Time Slot: 9:00 AM + Teacher ID: T1 + Subject ID: S1 + Classroom ID: Room 101 + + Section B: + Time Slot: 10:00 AM + Teacher ID: T2 + Subject ID: S2 + Classroom ID: Room 102 + + Section C: + Time Slot: 11:00 AM + Teacher ID: T3 + Subject ID: S3 + Classroom ID: Room 103 +""" + # Clean up any extra whitespace from the actual output + output = output.strip() + + # Assert if the output matches the expected output + self.assertEqual(output, expected_output.strip()) + + +if __name__ == "__main__": + unittest.main() diff --git a/test_suite/test_fitness.py b/test_suite/test_fitness.py new file mode 100644 index 0000000..75264f7 --- /dev/null +++ b/test_suite/test_fitness.py @@ -0,0 +1,797 @@ +# FITNESS + +# 1. def __init__(self, sections): + +import unittest + + +class Sections: + def __init__(self, num_sections): + # Generate sections as sectionA, sectionB, sectionC, etc. + self.sections = [f"section{chr(65 + i)}" for i in range(num_sections)] + + def __iter__(self): + # Make the class iterable by returning an iterator over the sections list + return iter(self.sections) + + def __len__(self): + # Optionally, define the length of the sections + return len(self.sections) + + def __getitem__(self, index): + # Optional: Enable indexing for the sections + return self.sections[index] + + +# TimetableFitness Class (to manage and calculate timetable fitness) +class TimetableFitness: + def __init__(self, sections): + # Ensure `self.sections` is iterable (convert Sections object to a list if needed) + if isinstance(sections, Sections): + self.sections = list( + sections + ) # Convert to list if it is a custom iterable object + else: + self.sections = sections # Assume it is already a list or other iterable + + # Mocking additional data attributes for the sake of this example + self.days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"] + self.time_slots = ["9-10 AM", "10-11 AM", "11-12 PM", "1-2 PM", "2-3 PM"] + + # Teacher-subject mapping + self.subject_teacher_map = { + "TCS-531": ["Teacher1", "Teacher2"], + "TCS-502": ["Teacher3", "Teacher4"], + } + + # Classroom and capacity details + self.classrooms = ["Room1", "Room2", "Room3"] + self.room_capacity = {"Room1": 30, "Room2": 25, "Room3": 40} + + # Data structures for tracking schedules + self.teacher_schedule = {slot: {} for slot in self.time_slots} + self.room_schedule = {slot: {} for slot in self.time_slots} + self.assigned_teachers = {section: {} for section in self.sections} + self.section_rooms = { + section: self.classrooms[i % len(self.classrooms)] + for i, section in enumerate(self.sections) + } + + # Subject-specific constraints + self.subject_weekly_quota = {"TCS-531": 3, "TCS-502": 3, "Placement_Class": 1} + + self.weekly_assignments = { + section: {subject: 0 for subject in self.subject_weekly_quota} + for section in self.sections + } + + # Teacher constraints + self.teacher_preferences = { + teacher_id: [1, 2, 3, 4, 5, 6, 7] + for teacher_id in ["Teacher1", "Teacher2", "Teacher3", "Teacher4"] + } + self.teacher_work_load = {teacher: 5 for teacher in self.teacher_preferences} + self.teacher_assignments = {} + + +# Unit Test Class +class TestTimetableFitness(unittest.TestCase): + def setUp(self): + # Create an instance of Sections with 5 sections + self.sections = Sections(5) + self.timetable_fitness = TimetableFitness(self.sections) + + def test_sections_iterable(self): + # Test that Sections object is iterable + sections_list = list(self.sections) + expected_sections = ["sectionA", "sectionB", "sectionC", "sectionD", "sectionE"] + self.assertEqual(sections_list, expected_sections) + + def test_section_rooms_mapping(self): + # Test that each section is mapped to a classroom + section_rooms = self.timetable_fitness.section_rooms + self.assertEqual(len(section_rooms), len(self.sections)) + self.assertTrue(all(section in section_rooms for section in self.sections)) + + def test_assigned_teachers_structure(self): + # Test that assigned teachers structure is initialized correctly + assigned_teachers = self.timetable_fitness.assigned_teachers + self.assertEqual(len(assigned_teachers), len(self.sections)) + self.assertTrue(all(section in assigned_teachers for section in self.sections)) + + +if __name__ == "__main__": + unittest.main() + +# 2. def generate_day_schedule(self, day, half_day_sections, week_number): + +import random +from collections import defaultdict + + +class TimetableGenerator: + def __init__(self, sections, time_slots, subject_teacher_map, section_rooms, lab): + self.sections = sections + self.time_slots = time_slots + self.subject_teacher_map = subject_teacher_map + self.section_rooms = section_rooms + self.lab = lab + self.weekly_assignments = defaultdict(lambda: defaultdict(int)) + self.teacher_schedule = defaultdict(dict) + self.room_schedule = defaultdict(dict) + self.teacher_assignments = defaultdict(dict) + + def generate_day_schedule(self, day, half_day_sections, week_number): + day_schedule = {} + subject_teacher_usage = { + subject: iter(teachers) + for subject, teachers in self.subject_teacher_map.items() + } + + for section in self.sections: + section_schedule = [] + subjects_used_today = set() + current_room = self.section_rooms[section] + num_slots = 4 if section in half_day_sections else 7 + + index = 1 + while index <= num_slots: + time_slot = self.time_slots[index] + + # Skip if time_slot is already used + if any(item["time_slot"] == time_slot for item in section_schedule): + index += 1 + continue + + available_subjects = list(self.subject_teacher_map.keys()) + subject, teacher = None, None + is_lab = False + + while available_subjects: + subject = random.choice(available_subjects) + + # Handle special rules for Placement_Class and labs + if subject == "Placement_Class" and index != 6: + available_subjects.remove(subject) + continue + + if "Lab" in subject: + if index + 1 > num_slots: # Ensure space for double slot + available_subjects.remove(subject) + continue + is_lab = True # Mark this as a lab + + if subject not in subjects_used_today: + teacher_iter = subject_teacher_usage[subject] + try: + teacher = next(teacher_iter) + except StopIteration: + teacher_iter = iter(self.subject_teacher_map[subject]) + teacher = next(teacher_iter) + subject_teacher_usage[subject] = teacher_iter + break + + available_subjects.remove(subject) + + if subject is None or teacher is None: + subject, teacher = "Library", "None" + + if subject != "Library": + self.weekly_assignments[section][subject] += 1 + + subjects_used_today.add(subject) + + # Assign rooms based on whether it's a lab + assigned_room = random.choice(self.lab) if is_lab else current_room + self.teacher_schedule[time_slot][teacher] = section + self.room_schedule[time_slot][assigned_room] = section + + # Handle double slot allocation for labs + if is_lab: + next_slot_index = index + 1 + if next_slot_index <= num_slots: + next_time_slot = self.time_slots[next_slot_index] + section_schedule.append( + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": assigned_room, + "time_slot": next_time_slot, + } + ) + self.room_schedule[next_time_slot][assigned_room] = section + self.teacher_schedule[next_time_slot][teacher] = section + index += 1 # Skip the next slot for lab + + section_schedule.append( + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": assigned_room, + "time_slot": time_slot, + } + ) + + index += 1 + + day_schedule[section] = section_schedule + return day_schedule + + +import unittest + + +class TestGenerateDaySchedule(unittest.TestCase): + def setUp(self): + self.sections = ["A", "B", "C"] + self.time_slots = { + 1: "9:00-10:00", + 2: "10:00-11:00", + 3: "11:00-12:00", + 4: "12:00-1:00", + 5: "2:00-3:00", + 6: "3:00-4:00", + 7: "4:00-5:00", + } + self.subject_teacher_map = { + "Math": ["T1", "T2"], + "Physics": ["T3"], + "Chemistry Lab": ["T4"], + "Placement_Class": ["T5"], + } + self.section_rooms = {"A": "R1", "B": "R2", "C": "R3"} + self.lab = ["Lab1", "Lab2"] + self.tt = TimetableGenerator( + self.sections, + self.time_slots, + self.subject_teacher_map, + self.section_rooms, + self.lab, + ) + + def test_schedule_structure(self): + schedule = self.tt.generate_day_schedule("Monday", ["A"], 1) + self.assertIsInstance(schedule, dict) + for section, section_schedule in schedule.items(): + self.assertIsInstance(section_schedule, list) + for entry in section_schedule: + self.assertIn("teacher_id", entry) + self.assertIn("subject_id", entry) + self.assertIn("classroom_id", entry) + self.assertIn("time_slot", entry) + + def test_half_day_slots(self): + schedule = self.tt.generate_day_schedule("Monday", ["A"], 1) + for section, section_schedule in schedule.items(): + if section == "A": + self.assertEqual(len(section_schedule), 4) + else: + self.assertEqual(len(section_schedule), 7) + + def test_no_repeated_time_slots(self): + schedule = self.tt.generate_day_schedule("Monday", ["A"], 1) + for section, section_schedule in schedule.items(): + time_slots = [entry["time_slot"] for entry in section_schedule] + self.assertEqual(len(time_slots), len(set(time_slots))) + + def test_lab_double_slots(self): + schedule = self.tt.generate_day_schedule("Monday", ["A"], 1) + for section, section_schedule in schedule.items(): + for i, entry in enumerate(section_schedule): + if "Lab" in entry["subject_id"]: + current_slot = entry["time_slot"] + current_index = list(self.tt.time_slots.values()).index( + current_slot + ) + + # Check if the lab occupies the next time slot + if current_index + 1 < len(self.tt.time_slots): + next_slot = section_schedule[i + 1]["time_slot"] + expected_next_slot = self.tt.time_slots[current_index + 1] + self.assertEqual( + next_slot, + expected_next_slot, + f"Lab for section {section} did not occupy consecutive slots as expected.", + ) + + def test_teacher_and_room_assignments(self): + schedule = self.tt.generate_day_schedule("Monday", ["A"], 1) + for section, section_schedule in schedule.items(): + for entry in section_schedule: + teacher = entry["teacher_id"] + room = entry["classroom_id"] + time_slot = entry["time_slot"] + self.assertIn(teacher, self.tt.teacher_schedule[time_slot]) + self.assertIn(room, self.tt.room_schedule[time_slot]) + + +if __name__ == "__main__": + unittest.main() + +# 3. def create_timetable(self, num_weeks=1): + +import random +import unittest +from collections import defaultdict + + +class TimetableGenerator: + def __init__(self, sections, time_slots, subject_teacher_map, section_rooms, lab): + self.sections = sections + self.time_slots = time_slots + self.subject_teacher_map = subject_teacher_map + self.section_rooms = section_rooms + self.lab = lab + self.days = [ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + ] # Ensure days are initialized + self.weekly_assignments = defaultdict(lambda: defaultdict(int)) + self.teacher_schedule = defaultdict(dict) + self.room_schedule = defaultdict(dict) + self.teacher_assignments = defaultdict(dict) + + def generate_day_schedule(self, day, half_day_sections, week_number): + day_schedule = {} + subject_teacher_usage = { + subject: iter(teachers) + for subject, teachers in self.subject_teacher_map.items() + } + + for section in self.sections: + section_schedule = [] + subjects_used_today = set() + current_room = self.section_rooms[section] + num_slots = 4 if section in half_day_sections else 7 + + for index in range(1, num_slots + 1): + time_slot = self.time_slots[index] + + # Skip if time_slot is already used + if any(item["time_slot"] == time_slot for item in section_schedule): + continue + + available_subjects = list(self.subject_teacher_map.keys()) + subject, teacher = None, None + is_lab = False + + while available_subjects: + subject = random.choice(available_subjects) + + # Handle special rules for Placement_Class and labs + if ( + subject == "Placement_Class" and index <= 4 + ): # Placement classes are after lunch + available_subjects.remove(subject) + continue + + if "Lab" in subject: + if index + 1 > num_slots: # Ensure space for double slot + available_subjects.remove(subject) + continue + is_lab = True # Mark this as a lab + + if subject not in subjects_used_today: + teacher_iter = subject_teacher_usage[subject] + try: + teacher = next(teacher_iter) + self.teacher_assignments.setdefault(section, {})[ + subject + ] = teacher + except StopIteration: + teacher_iter = iter(self.subject_teacher_map[subject]) + teacher = next(teacher_iter) + subject_teacher_usage[subject] = teacher_iter + self.teacher_assignments.setdefault(section, {})[ + subject + ] = teacher + + break + + available_subjects.remove(subject) + + if subject is None or teacher is None: + subject, teacher = "Library", "None" + + if subject != "Library": + self.weekly_assignments[section][subject] += 1 + + subjects_used_today.add(subject) + + # Assign rooms based on whether it's a lab + if is_lab: + assigned_room = random.choice(self.lab) + else: + assigned_room = current_room + + self.teacher_schedule[time_slot][teacher] = section + self.room_schedule[time_slot][assigned_room] = section + + # Handle double slot allocation for labs + if is_lab: + next_slot_index = index + 1 + if next_slot_index <= num_slots: + next_time_slot = self.time_slots[next_slot_index] + + # Assign consecutive slot to the lab + section_schedule.append( + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": assigned_room, + "time_slot": next_time_slot, + } + ) + self.room_schedule[next_time_slot][assigned_room] = section + self.teacher_schedule[next_time_slot][teacher] = section + index += 1 # Skip the next slot for lab + + section_schedule.append( + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": assigned_room, + "time_slot": time_slot, + } + ) + + day_schedule[section] = section_schedule + return day_schedule + + def create_timetable(self, num_weeks=1): + timetable = {} + for week in range(1, num_weeks + 1): + week_schedule = {} + for week_day in self.days: + half_day_sections = random.sample( + self.sections, len(self.sections) // 2 + ) + week_schedule[week_day] = self.generate_day_schedule( + week_day, half_day_sections, week + ) + timetable[f"Week {week}"] = week_schedule + return timetable + + +class TestTimetableGenerator(unittest.TestCase): + def setUp(self): + self.sections = ["A", "B", "C"] + self.time_slots = { + 1: "9:00-10:00", + 2: "10:00-11:00", + 3: "11:00-12:00", + 4: "12:00-1:00", + 5: "2:00-3:00", + 6: "3:00-4:00", + 7: "4:00-5:00", + } + self.subject_teacher_map = { + "Math": ["T1", "T2"], + "Physics": ["T3"], + "Chemistry Lab": ["T4"], + "Placement_Class": ["T5"], + } + self.section_rooms = {"A": "R1", "B": "R2", "C": "R3"} + self.lab = ["Lab1", "Lab2"] + self.tt = TimetableGenerator( + self.sections, + self.time_slots, + self.subject_teacher_map, + self.section_rooms, + self.lab, + ) + + def test_create_timetable(self): + timetable = self.tt.create_timetable(num_weeks=2) + self.assertIsInstance(timetable, dict) + self.assertGreater(len(timetable), 0) + self.assertIn("Week 1", timetable) + self.assertIn("Week 2", timetable) + + # Verify timetable structure + for week, week_schedule in timetable.items(): + self.assertIsInstance(week_schedule, dict) + for day, day_schedule in week_schedule.items(): + self.assertIsInstance(day_schedule, dict) + for section, section_schedule in day_schedule.items(): + self.assertIsInstance(section_schedule, list) + for entry in section_schedule: + self.assertIn("teacher_id", entry) + self.assertIn("subject_id", entry) + self.assertIn("classroom_id", entry) + self.assertIn("time_slot", entry) + + +if __name__ == "__main__": + unittest.main() + +# 4. def calculate_fitness(self, chromosome): + +import random +import unittest +from collections import defaultdict + + +class TimetableGenerator: + def __init__( + self, + sections, + time_slots, + subject_teacher_map, + section_rooms, + lab, + section_strength, + room_capacity, + teacher_preferences, + teacher_work_load, + ): + self.sections = sections + self.time_slots = time_slots + self.subject_teacher_map = subject_teacher_map + self.section_rooms = section_rooms + self.lab = lab + self.section_strength = section_strength + self.room_capacity = room_capacity + self.teacher_preferences = teacher_preferences + self.teacher_work_load = teacher_work_load + self.days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"] + self.weekly_assignments = defaultdict(lambda: defaultdict(int)) + self.teacher_schedule = defaultdict(dict) + self.room_schedule = defaultdict(dict) + self.teacher_assignments = defaultdict(dict) + + def generate_day_schedule(self, day, half_day_sections, week_number): + day_schedule = {} + subject_teacher_usage = { + subject: iter(teachers) + for subject, teachers in self.subject_teacher_map.items() + } + + for section in self.sections: + section_schedule = [] + subjects_used_today = set() + current_room = self.section_rooms[section] + num_slots = 4 if section in half_day_sections else 7 + + for index in range(1, num_slots + 1): + time_slot = self.time_slots[index] + + if any(item["time_slot"] == time_slot for item in section_schedule): + continue + + available_subjects = list(self.subject_teacher_map.keys()) + subject, teacher = None, None + is_lab = False + + while available_subjects: + subject = random.choice(available_subjects) + + if subject == "Placement_Class" and index <= 4: + available_subjects.remove(subject) + continue + + if "Lab" in subject: + if index + 1 > num_slots: + available_subjects.remove(subject) + continue + is_lab = True + + if subject not in subjects_used_today: + teacher_iter = subject_teacher_usage[subject] + try: + teacher = next(teacher_iter) + self.teacher_assignments.setdefault(section, {})[ + subject + ] = teacher + except StopIteration: + teacher_iter = iter(self.subject_teacher_map[subject]) + teacher = next(teacher_iter) + subject_teacher_usage[subject] = teacher_iter + self.teacher_assignments.setdefault(section, {})[ + subject + ] = teacher + + break + + available_subjects.remove(subject) + + if subject is None or teacher is None: + subject, teacher = "Library", "None" + + if subject != "Library": + self.weekly_assignments[section][subject] += 1 + + subjects_used_today.add(subject) + + if is_lab: + assigned_room = random.choice(self.lab) + else: + assigned_room = current_room + + self.teacher_schedule[time_slot][teacher] = section + self.room_schedule[time_slot][assigned_room] = section + + if is_lab: + next_slot_index = index + 1 + if next_slot_index <= num_slots: + next_time_slot = self.time_slots[next_slot_index] + section_schedule.append( + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": assigned_room, + "time_slot": next_time_slot, + } + ) + self.room_schedule[next_time_slot][assigned_room] = section + self.teacher_schedule[next_time_slot][teacher] = section + index += 1 + + section_schedule.append( + { + "teacher_id": teacher, + "subject_id": subject, + "classroom_id": assigned_room, + "time_slot": time_slot, + } + ) + + day_schedule[section] = section_schedule + return day_schedule + + def calculate_fitness(self, chromosome): + week_fitness_scores = [] + + for week_num, (week, week_schedule) in enumerate(chromosome.items(), start=1): + overall_fitness_score = 0 + section_fitness_scores = {} + + for day, day_schedule in week_schedule.items(): + section_fitness_scores[day] = {} + + for section, section_schedule in day_schedule.items(): + section_score = 100 + teacher_time_slots = {} + classroom_time_slots = {} + teacher_load = {} + + for item in section_schedule: + teacher = item["teacher_id"] + classroom = item["classroom_id"] + time_slot = item["time_slot"] + strength = self.section_strength.get(section, 0) + + if "Break" in time_slot: + continue + + if (teacher, time_slot) in teacher_time_slots: + section_score -= 30 + else: + teacher_time_slots[(teacher, time_slot)] = section + + if (classroom, time_slot) in classroom_time_slots: + section_score -= 20 + else: + classroom_time_slots[(classroom, time_slot)] = section + + if teacher not in teacher_load: + teacher_load[teacher] = [] + teacher_load[teacher].append(time_slot) + + if strength > self.room_capacity.get(classroom, 0): + section_score -= 25 + + preferred_slots = self.teacher_preferences.get(teacher, []) + if time_slot not in preferred_slots: + section_score -= 5 + + for teacher, time_slots in teacher_load.items(): + if len(time_slots) > self.teacher_work_load.get(teacher, 5): + section_score -= 10 + + section_fitness_scores[day][section] = section_score + overall_fitness_score += section_score + + week_fitness_scores.append( + {"week": f"Week {week_num}", "score": overall_fitness_score} + ) + + return week_fitness_scores, section_fitness_scores + + +class TestTimetableGenerator(unittest.TestCase): + def setUp(self): + self.sections = ["A", "B", "C"] + self.time_slots = { + 1: "9:00-10:00", + 2: "10:00-11:00", + 3: "11:00-12:00", + 4: "12:00-1:00", + 5: "2:00-3:00", + 6: "3:00-4:00", + 7: "4:00-5:00", + } + self.subject_teacher_map = { + "Math": ["T1", "T2"], + "Physics": ["T3"], + "Chemistry Lab": ["T4"], + "Placement_Class": ["T5"], + } + self.section_rooms = {"A": "R1", "B": "R2", "C": "R3"} + self.lab = ["Lab1", "Lab2"] + self.section_strength = {"A": 40, "B": 35, "C": 50} + self.room_capacity = {"R1": 40, "R2": 35, "R3": 50} + self.teacher_preferences = { + "T1": ["9:00-10:00", "3:00-4:00"], + "T2": ["10:00-11:00"], + } + self.teacher_work_load = {"T1": 10, "T2": 15, "T3": 8, "T4": 12, "T5": 6} + + self.tt = TimetableGenerator( + self.sections, + self.time_slots, + self.subject_teacher_map, + self.section_rooms, + self.lab, + self.section_strength, + self.room_capacity, + self.teacher_preferences, + self.teacher_work_load, + ) + + def test_calculate_fitness(self): + # Create a mock chromosome with timetable data + chromosome = { + "Week 1": { + "Monday": { + "A": [ + { + "teacher_id": "T1", + "subject_id": "Math", + "classroom_id": "R1", + "time_slot": "9:00-10:00", + } + ], + "B": [ + { + "teacher_id": "T3", + "subject_id": "Physics", + "classroom_id": "R2", + "time_slot": "10:00-11:00", + } + ], + }, + "Tuesday": { + "C": [ + { + "teacher_id": "T4", + "subject_id": "Chemistry Lab", + "classroom_id": "Lab1", + "time_slot": "11:00-12:00", + } + ], + }, + } + } + + week_fitness_scores, section_fitness_scores = self.tt.calculate_fitness( + chromosome + ) + + # Test the overall fitness score for Week 1 + self.assertIsInstance(week_fitness_scores, list) + self.assertGreater(len(week_fitness_scores), 0) + self.assertEqual(week_fitness_scores[0]["week"], "Week 1") + self.assertIn("score", week_fitness_scores[0]) + + # Test the section fitness score for a specific day and section + self.assertIn("Monday", section_fitness_scores) + self.assertIn("A", section_fitness_scores["Monday"]) + self.assertIsInstance(section_fitness_scores["Monday"]["A"], int) + + +if __name__ == "__main__": + unittest.main() diff --git a/test_suite/test_mutation.py b/test_suite/test_mutation.py new file mode 100644 index 0000000..c0fc280 --- /dev/null +++ b/test_suite/test_mutation.py @@ -0,0 +1,457 @@ +# MUTATION TESTS + +# 1. def mutate_time_slots_in_section(self, schedule, section): + +import random +import unittest + + +class TestMutateTimeSlots(unittest.TestCase): + def setUp(self): + # Initialize a mock schedule for testing + self.schedule = { + "SectionA": [ + {"subject": "Math", "time_slot": "9:00-10:00"}, + {"subject": "English", "time_slot": "10:00-11:00"}, + {"subject": "Science", "time_slot": "11:00-12:00"}, + ], + "SectionB": [ + {"subject": "History", "time_slot": "9:00-10:00"}, + ], + "SectionC": [], # Empty section + } + + def mutate_time_slots_in_section(self, schedule, section): + """ + Mutates the time slots within a particular section by shuffling them. + + Args: + schedule (dict): The schedule containing sections and their data. + section (str): The section in which mutation should occur. + + Returns: + bool: True if mutation was performed, False otherwise. + """ + if section in schedule: + section_list = schedule[section] + + if len(section_list) < 2: + return False + + time_slots = [entry["time_slot"] for entry in section_list] + random.shuffle(time_slots) + + for i, entry in enumerate(section_list): + entry["time_slot"] = time_slots[i] + + return True + return False + + def test_valid_mutation(self): + # Test a valid mutation for SectionA + original_schedule = [entry["time_slot"] for entry in self.schedule["SectionA"]] + result = self.mutate_time_slots_in_section(self.schedule, "SectionA") + self.assertTrue(result, "Mutation should occur with valid input") + mutated_schedule = [entry["time_slot"] for entry in self.schedule["SectionA"]] + + # Check if time slots were shuffled + self.assertNotEqual( + original_schedule, + mutated_schedule, + "Schedules should differ after mutation", + ) + self.assertCountEqual( + original_schedule, + mutated_schedule, + "Schedules should have the same elements after mutation", + ) + + def test_not_enough_time_slots(self): + # Test mutation for SectionB (not enough time slots to shuffle) + result = self.mutate_time_slots_in_section(self.schedule, "SectionB") + self.assertFalse( + result, "Mutation should not occur for single-element sections" + ) + + def test_empty_section(self): + # Test mutation for SectionC (empty section) + result = self.mutate_time_slots_in_section(self.schedule, "SectionC") + self.assertFalse(result, "Mutation should not occur for empty sections") + + def test_non_existent_section(self): + # Test mutation for a section not in the schedule + result = self.mutate_time_slots_in_section(self.schedule, "SectionD") + self.assertFalse(result, "Mutation should not occur for non-existent sections") + + +if __name__ == "__main__": + unittest.main(verbosity=2) + + +# 2. def mutate_schedule_for_week(self, weekly_schedule): + +import copy +import random +import unittest +from unittest.mock import patch + + +class Mutator: + def __init__(self, mutation_rate=0.5): + self.mutation_rate = mutation_rate + + def mutate_time_slots_in_section(self, schedule, section): + """ + Mutates the time slots within a particular section by shuffling them. + + Args: + schedule (dict): The schedule containing sections and their data. + section (str): The section in which mutation should occur. + + Returns: + bool: True if mutation was performed, False otherwise. + """ + if section in schedule: + section_list = schedule[section] + if len(section_list) < 2: + return False # Not enough time slots to shuffle. + + original_slots = [entry["time_slot"] for entry in section_list] + mutated_slots = original_slots[:] + attempt_count = 0 + + while ( + mutated_slots == original_slots and attempt_count < 5 + ): # Ensure mutation occurs + random.shuffle(mutated_slots) + attempt_count += 1 + + if mutated_slots != original_slots: + for i, entry in enumerate(section_list): + entry["time_slot"] = mutated_slots[i] + return True + + return False + + def mutate_schedule_for_week(self, weekly_schedule): + """ + Mutates the time slots for all weekdays in the weekly schedule. + + Args: + weekly_schedule (dict): The weekly schedule containing days, sections, and data. + + Returns: + dict: The mutated weekly schedule. + """ + mutated_weekly_schedule = copy.deepcopy(weekly_schedule) + has_mutation = False # Track if any mutation occurs + + for day, day_schedule in mutated_weekly_schedule.items(): + total_sections = list(day_schedule.keys()) + num_to_mutate = max(1, int(self.mutation_rate * len(total_sections))) + + sections_to_mutate = random.sample(total_sections, num_to_mutate) + + for section in sections_to_mutate: + if self.mutate_time_slots_in_section(day_schedule, section): + has_mutation = True + + if not has_mutation: # Ensure at least one mutation occurs + random_section = random.choice(list(mutated_weekly_schedule.values())) + random_section_name = random.choice(list(random_section.keys())) + self.mutate_time_slots_in_section(random_section, random_section_name) + + return mutated_weekly_schedule + + def print_weekly_schedule(self, weekly_schedule): + """ + Prints the weekly schedule in a readable format. + + Args: + weekly_schedule (dict): The weekly schedule to print. + """ + print("\nWeekly Schedule:") + print("=" * 50) + for day, day_schedule in weekly_schedule.items(): + print(f"Day: {day}") + print("-" * 30) + for section, entries in day_schedule.items(): + print(f" Section {section}:") + for entry in entries: + print( + f" Teacher: {entry['teacher_id']}, Subject: {entry['subject_id']}, " + f"Time Slot: {entry['time_slot']}" + ) + print("=" * 50) + + +class TestMutation(unittest.TestCase): + def setUp(self): + self.weekly_schedule = { + "Monday": { + "A": [ + { + "teacher_id": "T1", + "subject_id": "Math", + "time_slot": "9:00 - 9:55", + }, + { + "teacher_id": "T2", + "subject_id": "Physics", + "time_slot": "9:55 - 10:50", + }, + ], + "B": [ + { + "teacher_id": "T3", + "subject_id": "Chemistry", + "time_slot": "11:10 - 12:05", + }, + { + "teacher_id": "T4", + "subject_id": "Biology", + "time_slot": "12:05 - 1:00", + }, + ], + }, + "Tuesday": { + "A": [ + { + "teacher_id": "T5", + "subject_id": "History", + "time_slot": "1:20 - 2:15", + }, + ] + }, + } + self.mutator = Mutator() + + def test_default_mutation_rate(self): + self.assertEqual(self.mutator.mutation_rate, 0.5) + + def test_mutate_time_slots_in_section(self): + section_schedule = [ + {"teacher_id": "T1", "subject_id": "Math", "time_slot": "9:00 - 9:55"}, + {"teacher_id": "T2", "subject_id": "Physics", "time_slot": "9:55 - 10:50"}, + ] + original_schedule = copy.deepcopy(section_schedule) + result = self.mutator.mutate_time_slots_in_section({"A": section_schedule}, "A") + self.assertTrue(result) + mutated_schedule = section_schedule + self.assertNotEqual( + [entry["time_slot"] for entry in original_schedule], + [entry["time_slot"] for entry in mutated_schedule], + "Time slots were not mutated", + ) + + @patch("random.shuffle") + def test_mutate_schedule_for_week(self, mock_shuffle): + # Mock shuffle to ensure mutation logic works. + def mock_shuffle_fn(lst): + lst.reverse() + + mock_shuffle.side_effect = mock_shuffle_fn + + mutated_schedule = self.mutator.mutate_schedule_for_week(self.weekly_schedule) + self.assertIsInstance(mutated_schedule, dict) + + # Ensure the time slots are not identical + original_time_slots = [ + entry["time_slot"] + for day in self.weekly_schedule.values() + for section in day.values() + for entry in section + ] + mutated_time_slots = [ + entry["time_slot"] + for day in mutated_schedule.values() + for section in day.values() + for entry in section + ] + self.assertNotEqual( + original_time_slots, mutated_time_slots, "Mutation did not occur." + ) + + def test_print_weekly_schedule(self): + # This is primarily for manual verification; ensure no exceptions occur + self.mutator.print_weekly_schedule(self.weekly_schedule) + + +if __name__ == "__main__": + unittest.main() + + +# 3. def print_weekly_schedule(self, weekly_schedule): + +import copy +import random +import unittest +from unittest.mock import patch + +from Samples.Mutation_Files.mutation import Mutation + + +class TestMutation(unittest.TestCase): + def setUp(self): + """Setup a basic schedule for testing.""" + self.schedule = { + "Monday": { + "A": [ + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "9:55 - 10:50", + }, + { + "teacher_id": "AD08", + "subject_id": "PCS-506", + "classroom_id": "L5", + "time_slot": "9:00 - 9:55", + }, + ] + }, + "Tuesday": { + "A": [ + { + "teacher_id": "AK22", + "subject_id": "CSP-501", + "classroom_id": "R1", + "time_slot": "9:00 - 9:55", + } + ], + "B": [ + { + "teacher_id": "BJ16", + "subject_id": "TMA-502", + "classroom_id": "R2", + "time_slot": "9:55 - 10:50", + } + ], + }, + } + + def test_mutate_schedule_for_week(self): + """ + Test mutation of the entire week's schedule. + """ + mutation_rate = 0.5 # For testing, we can use a fixed mutation rate (50%) + mutator = Mutation(mutation_rate=mutation_rate) + + # Make a deep copy of the original schedule to compare later + original_schedule = copy.deepcopy(self.schedule) + + # Perform mutation + mutated_schedule = mutator.mutate_schedule_for_week(self.schedule) + + # Test if the mutated schedule is different from the original schedule + self.assertNotEqual( + self.schedule, + mutated_schedule, + "The mutated schedule should be different from the original schedule.", + ) + + # Test that at least one section was mutated + for day in mutated_schedule: + for section in mutated_schedule[day]: + # Check if time slots were shuffled (i.e., they're different from the original time slots) + for original_entry, mutated_entry in zip( + original_schedule[day][section], mutated_schedule[day][section] + ): + self.assertNotEqual( + original_entry["time_slot"], + mutated_entry["time_slot"], + f"Time slot for {section} on {day} should be mutated.", + ) + + def test_mutate_schedule_with_no_mutation(self): + """ + Test if mutation does not occur when the mutation rate is 0. + """ + mutation_rate = 0 # No mutation expected + mutator = Mutation(mutation_rate=mutation_rate) + + # Make a deep copy of the original schedule to compare later + original_schedule = copy.deepcopy(self.schedule) + + # Perform mutation + mutated_schedule = mutator.mutate_schedule_for_week(self.schedule) + + # Test that the mutated schedule is the same as the original schedule + self.assertEqual( + self.schedule, + mutated_schedule, + "The schedule should remain the same if mutation rate is 0.", + ) + + def test_mutate_schedule_with_max_mutation(self): + """ + Test if mutation affects all sections when mutation rate is 1. + """ + mutation_rate = 1 # Full mutation expected + mutator = Mutation(mutation_rate=mutation_rate) + + # Make a deep copy of the original schedule to compare later + original_schedule = copy.deepcopy(self.schedule) + + # Perform mutation + mutated_schedule = mutator.mutate_schedule_for_week(self.schedule) + + # Test that the mutated schedule is different from the original schedule + self.assertNotEqual( + self.schedule, + mutated_schedule, + "The mutated schedule should be different from the original schedule.", + ) + + # Check that all time slots were mutated + for day in mutated_schedule: + for section in mutated_schedule[day]: + for original_entry, mutated_entry in zip( + original_schedule[day][section], mutated_schedule[day][section] + ): + self.assertNotEqual( + original_entry["time_slot"], + mutated_entry["time_slot"], + f"Time slot for {section} on {day} should be mutated.", + ) + + @patch("random.sample") # Mock random.sample to control which sections get mutated + def test_mutate_schedule_with_controlled_mutation(self, mock_sample): + """ + Test mutation when we control which sections are selected for mutation. + """ + mutation_rate = 0.5 + mutator = Mutation(mutation_rate=mutation_rate) + + # Mock random.sample to return a controlled selection of sections to mutate + mock_sample.return_value = ["A"] # Only mutate section A + + # Make a deep copy of the original schedule to compare later + original_schedule = copy.deepcopy(self.schedule) + + # Perform mutation + mutated_schedule = mutator.mutate_schedule_for_week(self.schedule) + + # Test that only section A was mutated, and others remain the same + for day in mutated_schedule: + for section in mutated_schedule[day]: + for original_entry, mutated_entry in zip( + original_schedule[day][section], mutated_schedule[day][section] + ): + if section == "A": + self.assertNotEqual( + original_entry["time_slot"], + mutated_entry["time_slot"], + f"Time slot for {section} on {day} should be mutated.", + ) + else: + self.assertEqual( + original_entry["time_slot"], + mutated_entry["time_slot"], + f"Time slot for {section} on {day} should not be mutated.", + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/test_suite/test_selection.py b/test_suite/test_selection.py new file mode 100644 index 0000000..99e5861 --- /dev/null +++ b/test_suite/test_selection.py @@ -0,0 +1,421 @@ +# SELECTION TESTS + +# 1.def get_all_time_slots(): + +import unittest + +from Constants.time_intervals import TimeIntervalConstant + + +class TestTimeIntervalConstant(unittest.TestCase): + def test_get_all_time_slots(self): + expected_time_slots = [ + "9:00 - 9:55", + "9:55 - 10:50", + "11:10 - 12:05", + "12:05 - 1:00", + "1:20 - 2:15", + "2:15 - 3:10", + "3:30 - 4:25", + ] + self.assertEqual(TimeIntervalConstant.get_all_time_slots(), expected_time_slots) + + +if __name__ == "__main__": + unittest.main() + +import unittest +from unittest import mock + +from Samples.Selection_Files.selection import Timetable # Correct import + + +class TestTimetable(unittest.TestCase): + def setUp(self): + # Initialize the object to be tested + self.timetable = Timetable() # Initialize the correct class + + def test_generate_day_schedule_structure(self): + # Call the method + day_schedule = self.timetable.generate_day_schedule() + + # Check if the returned day_schedule is a dictionary with sections + self.assertIsInstance(day_schedule, dict) + self.assertGreater(len(day_schedule), 0) + + for section, schedule in day_schedule.items(): + self.assertIn( + section, self.timetable.sections + ) # Ensure sections are correct + self.assertIsInstance(schedule, list) + self.assertGreater(len(schedule), 0) + + def test_teacher_and_classroom_usage(self): + # Call the method + day_schedule = self.timetable.generate_day_schedule() + + # Check that no teacher is assigned to the same time slot more than once + time_slot_teacher_usage = { + time_slot: set() for time_slot in self.timetable.time_slots + } + time_slot_classroom_usage = { + time_slot: set() for time_slot in self.timetable.time_slots + } + + for section, schedule in day_schedule.items(): + for schedule_item in schedule: + if "Break" not in schedule_item["time_slot"]: + self.assertNotIn( + schedule_item["teacher_id"], + time_slot_teacher_usage[schedule_item["time_slot"]], + ) + self.assertNotIn( + schedule_item["classroom_id"], + time_slot_classroom_usage[schedule_item["time_slot"]], + ) + + # Track teacher and classroom usage + time_slot_teacher_usage[schedule_item["time_slot"]].add( + schedule_item["teacher_id"] + ) + time_slot_classroom_usage[schedule_item["time_slot"]].add( + schedule_item["classroom_id"] + ) + + def test_break_periods(self): + # Call the method + day_schedule = self.timetable.generate_day_schedule() + + # Check that "Break" is assigned correctly + break_found = False + for section, schedule in day_schedule.items(): + for schedule_item in schedule: + if "Break" in schedule_item["subject_id"]: + break_found = True + self.assertEqual(schedule_item["teacher_id"], "None") + self.assertEqual(schedule_item["classroom_id"], "N/A") + + self.assertTrue(break_found, "No break periods found in the schedule") + + +if __name__ == "__main__": + unittest.main() + + +# 2. def create_timetable(self): + +import unittest +from unittest import mock + +from Samples.Selection_Files.selection import Timetable + + +class TestTimetable(unittest.TestCase): + def setUp(self): + # Initialize the Timetable object + self.timetable = Timetable() + + def test_create_timetable_structure(self): + # Call the method + timetable = self.timetable.create_timetable() + + # Check if the timetable is a dictionary with days as keys + self.assertIsInstance(timetable, dict) + self.assertGreater(len(timetable), 0) # Ensure timetable is not empty + + # Verify that all days are present + for day in self.timetable.days: + self.assertIn(day, timetable) + + # Verify that each day contains a schedule (which is a dictionary) + for day, day_schedule in timetable.items(): + self.assertIsInstance(day_schedule, dict) + self.assertGreater(len(day_schedule), 0) + + def test_generate_day_schedule_called(self): + # Mock the generate_day_schedule method to check if it's called for each day + with mock.patch.object( + self.timetable, "generate_day_schedule", return_value={} + ) as mock_method: + self.timetable.create_timetable() + + # Check if generate_day_schedule was called for each day + self.assertEqual(mock_method.call_count, len(self.timetable.days)) + + def test_timetable_content(self): + # Call the method + timetable = self.timetable.create_timetable() + + # Check that each day's schedule contains sections + for day, day_schedule in timetable.items(): + for section, schedule in day_schedule.items(): + self.assertIn(section, self.timetable.sections) # Ensure valid sections + self.assertIsInstance(schedule, list) + self.assertGreater(len(schedule), 0) + + +if __name__ == "__main__": + unittest.main() + +# 3. def create_multiple_timelines(self, num_chromosomes): + +import unittest + +from Samples.Selection_Files.selection import ( + Timetable, +) # Adjust the import as necessary + + +class TestTimetable(unittest.TestCase): + def setUp(self): + # Initialize the object to be tested + self.timetable = ( + Timetable() + ) # Ensure this is the correct class that has create_multiple_timelines method + + def test_create_multiple_timelines(self): + num_chromosomes = 5 # You can change this number to test with different sizes + + # Call the method + timelines = self.timetable.create_multiple_timelines(num_chromosomes) + + # Check if the returned value is a list + self.assertIsInstance(timelines, list) + + # Check if the list has the correct number of timetables + self.assertEqual(len(timelines), num_chromosomes) + + # Optionally, check if each element in the list is a timetable (or has the correct structure) + for timetable in timelines: + self.assertIsInstance( + timetable, dict + ) # Assuming the timetable is a dictionary as in your previous methods + # Check that the timetable contains valid data, like sections + for section, schedule in timetable.items(): + self.assertIn(section, self.timetable.sections) + self.assertIsInstance(schedule, list) + self.assertGreater(len(schedule), 0) + + +if __name__ == "__main__": + unittest.main() + +# 4. def calculate_fitness(self, chromosome): + +import unittest + +from Samples.Selection_Files.selection import Timetable # Adjust the import path + + +class TestTimetableFitness(unittest.TestCase): + def setUp(self): + # Initialize the object to be tested + self.timetable = ( + Timetable() + ) # Ensure this is the class that has calculate_fitness method + + def test_fitness_calculation_valid_chromosome(self): + # Create a valid chromosome structure + chromosome = { + "Monday": { + "A": [ + { + "teacher_id": "T1", + "classroom_id": "R1", + "time_slot": "9:00-10:00", + "subject_id": "S1", + }, + { + "teacher_id": "T2", + "classroom_id": "R2", + "time_slot": "10:00-11:00", + "subject_id": "S2", + }, + { + "teacher_id": "T3", + "classroom_id": "R3", + "time_slot": "11:00-12:00", + "subject_id": "S3", + }, + { + "teacher_id": "None", + "classroom_id": "N/A", + "time_slot": "Break", + "subject_id": "Break", + }, + ], + "B": [ + { + "teacher_id": "T4", + "classroom_id": "R1", + "time_slot": "9:00-10:00", + "subject_id": "S4", + }, + { + "teacher_id": "T5", + "classroom_id": "R2", + "time_slot": "10:00-11:00", + "subject_id": "S5", + }, + { + "teacher_id": "T1", + "classroom_id": "R3", + "time_slot": "11:00-12:00", + "subject_id": "S6", + }, + { + "teacher_id": "None", + "classroom_id": "N/A", + "time_slot": "Break", + "subject_id": "Break", + }, + ], + } + } + + # Calculate fitness + fitness_score = self.timetable.calculate_fitness(chromosome) + + # Assert fitness score is within expected range + self.assertIsInstance(fitness_score, int) + self.assertGreaterEqual(fitness_score, 0) # Fitness should not be negative + + def test_fitness_calculation_with_violations(self): + # Create a chromosome with violations (e.g., overlapping time slots) + chromosome = { + "Monday": { + "A": [ + { + "teacher_id": "T1", + "classroom_id": "R1", + "time_slot": "9:00-10:00", + "subject_id": "S1", + }, + { + "teacher_id": "T1", + "classroom_id": "R2", + "time_slot": "9:00-10:00", + "subject_id": "S2", + }, # Teacher overlap + { + "teacher_id": "T3", + "classroom_id": "R3", + "time_slot": "11:00-12:00", + "subject_id": "S3", + }, + ], + "B": [ + { + "teacher_id": "T4", + "classroom_id": "R1", + "time_slot": "9:00-10:00", + "subject_id": "S4", + }, + { + "teacher_id": "T5", + "classroom_id": "R1", + "time_slot": "9:00-10:00", + "subject_id": "S5", + }, # Room overlap + ], + } + } + + # Calculate fitness + fitness_score = self.timetable.calculate_fitness(chromosome) + + # Assert fitness score accounts for penalties + self.assertIsInstance(fitness_score, int) + self.assertLess( + fitness_score, 100 + ) # Expect reduced fitness score due to violations + + def test_fitness_calculation_with_over_capacity(self): + # Create a chromosome where room capacity is exceeded + chromosome = { + "Monday": { + "A": [ + { + "teacher_id": "T1", + "classroom_id": "R3", + "time_slot": "9:00-10:00", + "subject_id": "S1", + }, # Over capacity + ] + } + } + + # Calculate fitness + fitness_score = self.timetable.calculate_fitness(chromosome) + + # Assert fitness score is reduced due to capacity violation + self.assertIsInstance(fitness_score, int) + self.assertLess( + fitness_score, 100 + ) # Expect reduced fitness score due to capacity penalty + + def test_fitness_calculation_no_timetable(self): + # Test with an empty chromosome + chromosome = {} + + # Calculate fitness + fitness_score = self.timetable.calculate_fitness(chromosome) + + # Assert fitness score is zero + self.assertEqual(fitness_score, 0) + + +if __name__ == "__main__": + unittest.main() + +# 5. def select_top_chromosomes(self, population: list, percentage=0.30) -> list: + +import random + + +def select_top_chromosomes(self, population: list, percentage=0.30) -> list: + num_to_select = int(len(population) * percentage) + fitness_scores = [ + (chromosome, self.calculate_fitness(chromosome)) for chromosome in population + ] + sorted_chromosomes = sorted(fitness_scores, key=lambda x: x[1], reverse=True) + + best_num = int(num_to_select * 0.20) + worst_num = int(num_to_select * 0.10) + middle_num = num_to_select - (best_num + worst_num) + + best_chromosomes = sorted_chromosomes[:best_num] + worst_chromosomes = sorted_chromosomes[-worst_num:] + middle_chromosomes = sorted_chromosomes[best_num : best_num + middle_num] + + roulette_num = int(middle_num * 0.70) + rank_num = middle_num - roulette_num + + # Handle empty middle_chromosomes for roulette selection + if middle_chromosomes: + total_fitness = sum(fitness for _, fitness in middle_chromosomes) + roulette_chromosomes = random.choices( + middle_chromosomes, + weights=[fitness / total_fitness for _, fitness in middle_chromosomes], + k=roulette_num, + ) + else: + roulette_chromosomes = [] + + if middle_chromosomes: + total_rank = sum(range(1, len(middle_chromosomes) + 1)) + rank_probabilities = [ + i / total_rank for i in range(1, len(middle_chromosomes) + 1) + ] + rank_chromosomes = random.choices( + middle_chromosomes, weights=rank_probabilities, k=rank_num + ) + else: + rank_chromosomes = [] + + selected_chromosomes = [chromosome for chromosome, _ in best_chromosomes] + selected_chromosomes += [chromosome for chromosome, _ in worst_chromosomes] + selected_chromosomes += [chromosome for chromosome, _ in roulette_chromosomes] + selected_chromosomes += [chromosome for chromosome, _ in rank_chromosomes] + + return selected_chromosomes diff --git a/test_suite/unit_test.py b/test_suite/unit_test.py new file mode 100644 index 0000000..e69de29 diff --git a/visualizer.py b/visualizer.py new file mode 100644 index 0000000..04b3efe --- /dev/null +++ b/visualizer.py @@ -0,0 +1,64 @@ +import pandas as pd + +# Suppose your JSON-like data is stored in a variable called 'timetable' +timetable = {'Monday': {'D': [{'teacher_id': 'AK23', 'subject_id': 'CSP-501', 'classroom_id': 'R5', 'time_slot': '9:00 - 9:55', 'group': 'all'}, {'teacher_id': 'SP06', 'subject_id': 'TCS-503', 'classroom_id': 'R5', 'time_slot': '9:55 - 10:50', 'group': 'all'}, {'teacher_id': 'SJ16', 'subject_id': 'TCS-509', 'classroom_id': 'R5', 'time_slot': '11:10 - 12:05', 'group': 'all'}, {'teacher_id': 'DT20', 'subject_id': 'XCS-501', 'classroom_id': 'R5', 'time_slot': '12:05 - 1:00', 'group': 'all'}], 'C': [{'teacher_id': 'AB01', 'subject_id': 'TCS-531', 'classroom_id': 'R3', 'time_slot': '9:00 - 9:55', 'group': 'all'}, {'teacher_id': 'DP07', 'subject_id': 'TCS-503', 'classroom_id': 'R3', 'time_slot': '9:55 - 10:50', 'group': 'all'}, {'teacher_id': 'AP24', 'subject_id': 'SCS-501', 'classroom_id': 'R3', 'time_slot': '11:10 - 12:05', 'group': 'all'}, {'teacher_id': 'SS03', 'subject_id': 'TCS-502', 'classroom_id': 'R3', 'time_slot': '12:05 - 1:00', 'group': 'all'}], 'A': [{'teacher_id': 'AB01', 'subject_id': 'TCS-531', 'classroom_id': 'R2', 'time_slot': '9:00 - 9:55', 'group': 'all'}, {'teacher_id': 'HP18', 'subject_id': 'TMA-502', 'classroom_id': 'R2', 'time_slot': '9:55 - 10:50', 'group': 'all'}, {'teacher_id': 'AD08', 'subject_id': 'PCS-506', 'classroom_id': 'L1', 'time_slot': '11:10 - 12:05', 'group': 1}, {'teacher_id': 'AD08', 'subject_id': 'PCS-506', 'classroom_id': 'L1', 'time_slot': '12:05 - 1:00', 'group': 1}, {'teacher_id': 'AD08', 'subject_id': 'PCS-506', 'classroom_id': 'L2', 'time_slot': '11:10 - 12:05', 'group': 2}, {'teacher_id': 'AD08', 'subject_id': 'PCS-506', 'classroom_id': 'L2', 'time_slot': '12:05 - 1:00', 'group': 2}, {'teacher_id': 'AA04', 'subject_id': 'TCS-502', 'classroom_id': 'R2', 'time_slot': '1:20 - 2:15', 'group': 'all'}, {'teacher_id': 'AC05', 'subject_id': 'TCS-503', 'classroom_id': 'R2', 'time_slot': '2:15 - 3:10', 'group': 'all'}, {'teacher_id': 'AK23', 'subject_id': 'CSP-501', 'classroom_id': 'R2', 'time_slot': '3:30 - 4:25', 'group': 'all'}], 'B': [{'teacher_id': 'PM14', 'subject_id': 'PMA-502', 'classroom_id': 'L1', 'time_slot': '9:00 - 9:55', 'group': 1}, {'teacher_id': 'PM14', 'subject_id': 'PMA-502', 'classroom_id': 'L1', 'time_slot': '9:55 - 10:50', 'group': 1}, {'teacher_id': 'PM14', 'subject_id': 'PMA-502', 'classroom_id': 'L2', 'time_slot': '9:00 - 9:55', 'group': 2}, {'teacher_id': 'PM14', 'subject_id': 'PMA-502', 'classroom_id': 'L2', 'time_slot': '9:55 - 10:50', 'group': 2}, {'teacher_id': 'BJ10', 'subject_id': 'TMA-502', 'classroom_id': 'R4', 'time_slot': '11:10 - 12:05', 'group': 'all'}, {'teacher_id': 'BJ10', 'subject_id': 'TCS-503', 'classroom_id': 'R4', 'time_slot': '12:05 - 1:00', 'group': 'all'}, {'teacher_id': 'AP24', 'subject_id': 'SCS-501', 'classroom_id': 'R4', 'time_slot': '1:20 - 2:15', 'group': 'all'}, {'teacher_id': 'PK02', 'subject_id': 'TCS-531', 'classroom_id': 'R4', 'time_slot': '2:15 - 3:10', 'group': 'all'}, {'teacher_id': 'SS03', 'subject_id': 'TCS-502', 'classroom_id': 'R4', 'time_slot': '3:30 - 4:25', 'group': 'all'}]}, 'Tuesday': {'B': [{'teacher_id': 'BJ10', 'subject_id': 'TCS-503', 'classroom_id': 'R4', 'time_slot': '9:00 - 9:55', 'group': 'all'}, {'teacher_id': 'AB01', 'subject_id': 'TCS-531', 'classroom_id': 'R4', 'time_slot': '9:55 - 10:50', 'group': 'all'}, {'teacher_id': 'DT20', 'subject_id': 'XCS-501', 'classroom_id': 'R4', 'time_slot': '11:10 - 12:05', 'group': 'all'}, {'teacher_id': 'SS03', 'subject_id': 'TCS-502', 'classroom_id': 'R4', 'time_slot': '12:05 - 1:00', 'group': 'all'}], 'D': [{'teacher_id': 'SJ16', 'subject_id': 'TCS-509', 'classroom_id': 'R5', 'time_slot': '9:00 - 9:55', 'group': 'all'}, {'teacher_id': 'AA04', 'subject_id': 'TCS-502', 'classroom_id': 'R5', 'time_slot': '9:55 - 10:50', 'group': 'all'}, {'teacher_id': 'RS11', 'subject_id': 'PCS-503', 'classroom_id': 'L1', 'time_slot': '11:10 - 12:05', 'group': 1}, {'teacher_id': 'RS11', 'subject_id': 'PCS-503', 'classroom_id': 'L1', 'time_slot': '12:05 - 1:00', 'group': 1}, {'teacher_id': 'RS11', 'subject_id': 'PCS-503', 'classroom_id': 'L2', 'time_slot': '11:10 - 12:05', 'group': 2}, {'teacher_id': 'RS11', 'subject_id': 'PCS-503', 'classroom_id': 'L2', 'time_slot': '12:05 - 1:00', 'group': 2}], 'C': [{'teacher_id': 'JM12', 'subject_id': 'TMA-502', 'classroom_id': 'R3', 'time_slot': '9:00 - 9:55', 'group': 'all'}, {'teacher_id': 'AB17', 'subject_id': 'TCS-509', 'classroom_id': 'R3', 'time_slot': '9:55 - 10:50', 'group': 'all'}, {'teacher_id': 'PK02', 'subject_id': 'TCS-531', 'classroom_id': 'R3', 'time_slot': '11:10 - 12:05', 'group': 'all'}, {'teacher_id': 'AK23', 'subject_id': 'CSP-501', 'classroom_id': 'R3', 'time_slot': '12:05 - 1:00', 'group': 'all'}, {'teacher_id': 'SP06', 'subject_id': 'TCS-503', 'classroom_id': 'R3', 'time_slot': '1:20 - 2:15', 'group': 'all'}, {'teacher_id': 'PA21', 'subject_id': 'XCS-501', 'classroom_id': 'R3', 'time_slot': '2:15 - 3:10', 'group': 'all'}, {'teacher_id': 'AC05', 'subject_id': 'TCS-502', 'classroom_id': 'R3', 'time_slot': '3:30 - 4:25', 'group': 'all'}], 'A': [{'teacher_id': 'DP07', 'subject_id': 'PCS-503', 'classroom_id': 'L1', 'time_slot': '9:00 - 9:55', 'group': 1}, {'teacher_id': 'DP07', 'subject_id': 'PCS-503', 'classroom_id': 'L1', 'time_slot': '9:55 - 10:50', 'group': 1}, {'teacher_id': 'DP07', 'subject_id': 'PCS-503', 'classroom_id': 'L2', 'time_slot': '9:00 - 9:55', 'group': 2}, {'teacher_id': 'DP07', 'subject_id': 'PCS-503', 'classroom_id': 'L2', 'time_slot': '9:55 - 10:50', 'group': 2}, {'teacher_id': 'SS03', 'subject_id': 'TCS-502', 'classroom_id': 'R2', 'time_slot': '11:10 - 12:05', 'group': 'all'}, {'teacher_id': 'SP06', 'subject_id': 'TCS-503', 'classroom_id': 'R2', 'time_slot': '12:05 - 1:00', 'group': 'all'}, {'teacher_id': 'AB01', 'subject_id': 'TCS-531', 'classroom_id': 'R2', 'time_slot': '1:20 - 2:15', 'group': 'all'}, {'teacher_id': 'HP18', 'subject_id': 'TMA-502', 'classroom_id': 'R2', 'time_slot': '2:15 - 3:10', 'group': 'all'}, {'teacher_id': 'AP24', 'subject_id': 'SCS-501', 'classroom_id': 'R2', 'time_slot': '3:30 - 4:25', 'group': 'all'}]}, 'Wednesday': {'C': [{'teacher_id': 'AD08', 'subject_id': 'PCS-506', 'classroom_id': 'L1', 'time_slot': '9:00 - 9:55', 'group': 1}, {'teacher_id': 'AD08', 'subject_id': 'PCS-506', 'classroom_id': 'L1', 'time_slot': '9:55 - 10:50', 'group': 1}, {'teacher_id': 'AD08', 'subject_id': 'PCS-506', 'classroom_id': 'L2', 'time_slot': '9:00 - 9:55', 'group': 2}, {'teacher_id': 'AD08', 'subject_id': 'PCS-506', 'classroom_id': 'L2', 'time_slot': '9:55 - 10:50', 'group': 2}, {'teacher_id': 'SJ16', 'subject_id': 'TCS-509', 'classroom_id': 'R3', 'time_slot': '11:10 - 12:05', 'group': 'all'}, {'teacher_id': 'SP06', 'subject_id': 'TCS-503', 'classroom_id': 'R3', 'time_slot': '12:05 - 1:00', 'group': 'all'}], 'A': [{'teacher_id': 'AB17', 'subject_id': 'TCS-509', 'classroom_id': 'R2', 'time_slot': '9:00 - 9:55', 'group': 'all'}, {'teacher_id': 'DP07', 'subject_id': 'TCS-503', 'classroom_id': 'R2', 'time_slot': '9:55 - 10:50', 'group': 'all'}, {'teacher_id': 'AK26', 'subject_id': 'Placement_Class', 'classroom_id': 'L1', 'time_slot': '11:10 - 12:05', 'group': 1}, {'teacher_id': 'AK26', 'subject_id': 'Placement_Class', 'classroom_id': 'L1', 'time_slot': '12:05 - 1:00', 'group': 1}, {'teacher_id': 'AK26', 'subject_id': 'Placement_Class', 'classroom_id': 'L2', 'time_slot': '11:10 - 12:05', 'group': 2}, {'teacher_id': 'AK26', 'subject_id': 'Placement_Class', 'classroom_id': 'L2', 'time_slot': '12:05 - 1:00', 'group': 2}], 'B': [{'teacher_id': 'BJ10', 'subject_id': 'TCS-503', 'classroom_id': 'R4', 'time_slot': '9:00 - 9:55', 'group': 'all'}, {'teacher_id': 'SS03', 'subject_id': 'TCS-502', 'classroom_id': 'R4', 'time_slot': '9:55 - 10:50', 'group': 'all'}, {'teacher_id': 'RS11', 'subject_id': 'PCS-503', 'classroom_id': 'L3', 'time_slot': '11:10 - 12:05', 'group': 1}, {'teacher_id': 'RS11', 'subject_id': 'PCS-503', 'classroom_id': 'L3', 'time_slot': '12:05 - 1:00', 'group': 1}, {'teacher_id': 'RS11', 'subject_id': 'PCS-503', 'classroom_id': 'L4', 'time_slot': '11:10 - 12:05', 'group': 2}, {'teacher_id': 'RS11', 'subject_id': 'PCS-503', 'classroom_id': 'L4', 'time_slot': '12:05 - 1:00', 'group': 2}, {'teacher_id': 'HP18', 'subject_id': 'TCS-509', 'classroom_id': 'R4', 'time_slot': '1:20 - 2:15', 'group': 'all'}, {'teacher_id': 'DT20', 'subject_id': 'XCS-501', 'classroom_id': 'R4', 'time_slot': '2:15 - 3:10', 'group': 'all'}, {'teacher_id': 'AB01', 'subject_id': 'TCS-531', 'classroom_id': 'R4', 'time_slot': '3:30 - 4:25', 'group': 'all'}], 'D': [{'teacher_id': 'AK26', 'subject_id': 'Placement_Class', 'classroom_id': 'L3', 'time_slot': '9:00 - 9:55', 'group': 1}, {'teacher_id': 'AK26', 'subject_id': 'Placement_Class', 'classroom_id': 'L3', 'time_slot': '9:55 - 10:50', 'group': 1}, {'teacher_id': 'AK26', 'subject_id': 'Placement_Class', 'classroom_id': 'L4', 'time_slot': '9:00 - 9:55', 'group': 2}, {'teacher_id': 'AK26', 'subject_id': 'Placement_Class', 'classroom_id': 'L4', 'time_slot': '9:55 - 10:50', 'group': 2}, {'teacher_id': 'SG19', 'subject_id': 'TCS-509', 'classroom_id': 'R5', 'time_slot': '11:10 - 12:05', 'group': 'all'}, {'teacher_id': 'JM12', 'subject_id': 'TMA-502', 'classroom_id': 'R5', 'time_slot': '12:05 - 1:00', 'group': 'all'}, {'teacher_id': 'PA21', 'subject_id': 'XCS-501', 'classroom_id': 'R5', 'time_slot': '1:20 - 2:15', 'group': 'all'}, {'teacher_id': 'AP24', 'subject_id': 'SCS-501', 'classroom_id': 'R5', 'time_slot': '2:15 - 3:10', 'group': 'all'}, {'teacher_id': 'PK02', 'subject_id': 'TCS-531', 'classroom_id': 'R5', 'time_slot': '3:30 - 4:25', 'group': 'all'}]}, 'Thursday': {'A': [{'teacher_id': 'SS03', 'subject_id': 'TCS-502', 'classroom_id': 'R2', 'time_slot': '9:00 - 9:55', 'group': 'all'}, {'teacher_id': 'SJ16', 'subject_id': 'TCS-509', 'classroom_id': 'R2', 'time_slot': '9:55 - 10:50', 'group': 'all'}, {'teacher_id': 'AB01', 'subject_id': 'TCS-531', 'classroom_id': 'R2', 'time_slot': '11:10 - 12:05', 'group': 'all'}, {'teacher_id': 'HP18', 'subject_id': 'TMA-502', 'classroom_id': 'R2', 'time_slot': '12:05 - 1:00', 'group': 'all'}], 'D': [{'teacher_id': 'AA04', 'subject_id': 'TCS-502', 'classroom_id': 'R5', 'time_slot': '9:00 - 9:55', 'group': 'all'}, {'teacher_id': 'SP06', 'subject_id': 'TCS-503', 'classroom_id': 'R5', 'time_slot': '9:55 - 10:50', 'group': 'all'}, {'teacher_id': 'PK02', 'subject_id': 'TCS-531', 'classroom_id': 'R5', 'time_slot': '11:10 - 12:05', 'group': 'all'}, {'teacher_id': 'BJ10', 'subject_id': 'TMA-502', 'classroom_id': 'R5', 'time_slot': '12:05 - 1:00', 'group': 'all'}], 'C': [{'teacher_id': 'AC05', 'subject_id': 'TCS-502', 'classroom_id': 'R3', 'time_slot': '9:00 - 9:55', 'group': 'all'}, {'teacher_id': 'AB17', 'subject_id': 'TCS-509', 'classroom_id': 'R3', 'time_slot': '9:55 - 10:50', 'group': 'all'}, {'teacher_id': 'AK26', 'subject_id': 'Placement_Class', 'classroom_id': 'L1', 'time_slot': '11:10 - 12:05', 'group': 1}, {'teacher_id': 'AK26', 'subject_id': 'Placement_Class', 'classroom_id': 'L1', 'time_slot': '12:05 - 1:00', 'group': 1}, {'teacher_id': 'AK26', 'subject_id': 'Placement_Class', 'classroom_id': 'L2', 'time_slot': '11:10 - 12:05', 'group': 2}, {'teacher_id': 'AK26', 'subject_id': 'Placement_Class', 'classroom_id': 'L2', 'time_slot': '12:05 - 1:00', 'group': 2}, {'teacher_id': 'PM14', 'subject_id': 'PMA-502', 'classroom_id': 'L1', 'time_slot': '1:20 - 2:15', 'group': 1}, {'teacher_id': 'PM14', 'subject_id': 'PMA-502', 'classroom_id': 'L1', 'time_slot': '2:15 - 3:10', 'group': 1}, {'teacher_id': 'PM14', 'subject_id': 'PMA-502', 'classroom_id': 'L2', 'time_slot': '1:20 - 2:15', 'group': 2}, {'teacher_id': 'PM14', 'subject_id': 'PMA-502', 'classroom_id': 'L2', 'time_slot': '2:15 - 3:10', 'group': 2}, {'teacher_id': 'DT20', 'subject_id': 'XCS-501', 'classroom_id': 'R3', 'time_slot': '3:30 - 4:25', 'group': 'all'}], 'B': [{'teacher_id': 'AK23', 'subject_id': 'CSP-501', 'classroom_id': 'R4', 'time_slot': '9:00 - 9:55', 'group': 'all'}, {'teacher_id': 'SG19', 'subject_id': 'TCS-509', 'classroom_id': 'R4', 'time_slot': '9:55 - 10:50', 'group': 'all'}, {'teacher_id': 'AD08', 'subject_id': 'PCS-506', 'classroom_id': 'L3', 'time_slot': '11:10 - 12:05', 'group': 1}, {'teacher_id': 'AD08', 'subject_id': 'PCS-506', 'classroom_id': 'L3', 'time_slot': '12:05 - 1:00', 'group': 1}, {'teacher_id': 'AD08', 'subject_id': 'PCS-506', 'classroom_id': 'L4', 'time_slot': '11:10 - 12:05', 'group': 2}, {'teacher_id': 'AD08', 'subject_id': 'PCS-506', 'classroom_id': 'L4', 'time_slot': '12:05 - 1:00', 'group': 2}, {'teacher_id': 'AK26', 'subject_id': 'Placement_Class', 'classroom_id': 'L3', 'time_slot': '1:20 - 2:15', 'group': 1}, {'teacher_id': 'AK26', 'subject_id': 'Placement_Class', 'classroom_id': 'L3', 'time_slot': '2:15 - 3:10', 'group': 1}, {'teacher_id': 'AK26', 'subject_id': 'Placement_Class', 'classroom_id': 'L4', 'time_slot': '1:20 - 2:15', 'group': 2}, {'teacher_id': 'AK26', 'subject_id': 'Placement_Class', 'classroom_id': 'L4', 'time_slot': '2:15 - 3:10', 'group': 2}, {'teacher_id': 'RS11', 'subject_id': 'TMA-502', 'classroom_id': 'R4', 'time_slot': '3:30 - 4:25', 'group': 'all'}]}, 'Friday': {'B': [{'teacher_id': 'BJ10', 'subject_id': 'TMA-502', 'classroom_id': 'R4', 'time_slot': '9:00 - 9:55', 'group': 'all'}, {'teacher_id': 'SJ16', 'subject_id': 'TCS-509', 'classroom_id': 'R4', 'time_slot': '9:55 - 10:50', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R4', 'time_slot': '11:10 - 12:05', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R4', 'time_slot': '12:05 - 1:00', 'group': 'all'}], 'D': [{'teacher_id': 'AD08', 'subject_id': 'PCS-506', 'classroom_id': 'L1', 'time_slot': '9:00 - 9:55', 'group': 1}, {'teacher_id': 'AD08', 'subject_id': 'PCS-506', 'classroom_id': 'L1', 'time_slot': '9:55 - 10:50', 'group': 1}, {'teacher_id': 'AD08', 'subject_id': 'PCS-506', 'classroom_id': 'L2', 'time_slot': '9:00 - 9:55', 'group': 2}, {'teacher_id': 'AD08', 'subject_id': 'PCS-506', 'classroom_id': 'L2', 'time_slot': '9:55 - 10:50', 'group': 2}, {'teacher_id': 'SP06', 'subject_id': 'TCS-503', 'classroom_id': 'R5', 'time_slot': '11:10 - 12:05', 'group': 'all'}, {'teacher_id': 'AB01', 'subject_id': 'TCS-531', 'classroom_id': 'R5', 'time_slot': '12:05 - 1:00', 'group': 'all'}], 'A': [{'teacher_id': 'PM14', 'subject_id': 'PMA-502', 'classroom_id': 'L3', 'time_slot': '9:00 - 9:55', 'group': 1}, {'teacher_id': 'PM14', 'subject_id': 'PMA-502', 'classroom_id': 'L3', 'time_slot': '9:55 - 10:50', 'group': 1}, {'teacher_id': 'PM14', 'subject_id': 'PMA-502', 'classroom_id': 'L4', 'time_slot': '9:00 - 9:55', 'group': 2}, {'teacher_id': 'PM14', 'subject_id': 'PMA-502', 'classroom_id': 'L4', 'time_slot': '9:55 - 10:50', 'group': 2}, {'teacher_id': 'DT20', 'subject_id': 'XCS-501', 'classroom_id': 'R2', 'time_slot': '11:10 - 12:05', 'group': 'all'}, {'teacher_id': 'AB17', 'subject_id': 'TCS-509', 'classroom_id': 'R2', 'time_slot': '12:05 - 1:00', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R2', 'time_slot': '1:20 - 2:15', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R2', 'time_slot': '2:15 - 3:10', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R2', 'time_slot': '3:30 - 4:25', 'group': 'all'}], 'C': [{'teacher_id': 'PK02', 'subject_id': 'TCS-531', 'classroom_id': 'R3', 'time_slot': '9:00 - 9:55', 'group': 'all'}, {'teacher_id': 'RS11', 'subject_id': 'TMA-502', 'classroom_id': 'R3', 'time_slot': '9:55 - 10:50', 'group': 'all'}, {'teacher_id': 'DP07', 'subject_id': 'PCS-503', 'classroom_id': 'L1', 'time_slot': '11:10 - 12:05', 'group': 1}, {'teacher_id': 'DP07', 'subject_id': 'PCS-503', 'classroom_id': 'L1', 'time_slot': '12:05 - 1:00', 'group': 1}, {'teacher_id': 'DP07', 'subject_id': 'PCS-503', 'classroom_id': 'L2', 'time_slot': '11:10 - 12:05', 'group': 2}, {'teacher_id': 'DP07', 'subject_id': 'PCS-503', 'classroom_id': 'L2', 'time_slot': '12:05 - 1:00', 'group': 2}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R3', 'time_slot': '1:20 - 2:15', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R3', 'time_slot': '2:15 - 3:10', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R3', 'time_slot': '3:30 - 4:25', 'group': 'all'}]}, 'Saturday': {'D': [{'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R5', 'time_slot': '9:00 - 9:55', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R5', 'time_slot': '9:55 - 10:50', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R5', 'time_slot': '11:10 - 12:05', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R5', 'time_slot': '12:05 - 1:00', 'group': 'all'}], 'A': [{'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R2', 'time_slot': '9:00 - 9:55', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R2', 'time_slot': '9:55 - 10:50', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R2', 'time_slot': '11:10 - 12:05', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R2', 'time_slot': '12:05 - 1:00', 'group': 'all'}], 'C': [{'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R3', 'time_slot': '9:00 - 9:55', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R3', 'time_slot': '9:55 - 10:50', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R3', 'time_slot': '11:10 - 12:05', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R3', 'time_slot': '12:05 - 1:00', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R3', 'time_slot': '1:20 - 2:15', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R3', 'time_slot': '2:15 - 3:10', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R3', 'time_slot': '3:30 - 4:25', 'group': 'all'}], 'B': [{'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R4', 'time_slot': '9:00 - 9:55', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R4', 'time_slot': '9:55 - 10:50', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R4', 'time_slot': '11:10 - 12:05', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R4', 'time_slot': '12:05 - 1:00', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R4', 'time_slot': '1:20 - 2:15', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R4', 'time_slot': '2:15 - 3:10', 'group': 'all'}, {'teacher_id': 'None', 'subject_id': 'Library', 'classroom_id': 'R4', 'time_slot': '3:30 - 4:25', 'group': 'all'}]}} +days_order = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] +time_order = [ + "9:00 - 9:55", + "9:55 - 10:50", + "11:10 - 12:05", + "12:05 - 1:00", + "1:20 - 2:15", + "2:15 - 3:10", + "3:30 - 4:25", +] + +def get_section_table(timetable, section): + rows = [] + for day, day_data in timetable.items(): + if section in day_data: + for slot in day_data[section]: + subj = slot["subject_id"] + teacher = slot["teacher_id"] + rows.append( + { + "Day": day, + "Time": slot["time_slot"], + "Subject": f"{subj} ({teacher})", + } + ) + + df = pd.DataFrame(rows) + if df.empty: + return pd.DataFrame() + + df["Day"] = pd.Categorical(df["Day"], categories=days_order, ordered=True) + df["Time"] = pd.Categorical(df["Time"], categories=time_order, ordered=True) + + pivoted = ( + df.pivot_table( + index="Day", + columns="Time", + values="Subject", + aggfunc=lambda x: " / ".join(x) # join duplicates if multiple groups + ) + .fillna("") + ) + return pivoted + + +# 🔹 Collect all sections dynamically from all days +sections = sorted({sec for day in timetable.values() for sec in day.keys()}) + +# Create a single HTML string with all section timetables +html_content = "All Sections Timetable" +for section in sections: + table = get_section_table(timetable, section) + if not table.empty: + html_content += f"

Section {section}

" + html_content += table.to_html() +html_content += "" + +# Write the combined HTML to a file +with open("all_sections_timetable.html", "w", encoding="utf-8") as f: + f.write(html_content) \ No newline at end of file