├── .gitignore
├── DummyFiles
├── CN-SDN.pdf
├── GradAlgoNotes.pdf
└── IntroToMachineLearning.pdf
├── LICENSE
├── README.md
├── backend
├── README.md
├── api
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_course_professor_semester_topic_peeruser_last_poll_and_more.py
│ │ ├── 0003_file_course_alter_file_original_author_and_more.py
│ │ ├── 0004_course_number_alter_course_name.py
│ │ ├── 0005_file_created_at.py
│ │ ├── 0006_file_points_peeruser_points.py
│ │ ├── 0007_remove_file_points_file_downvotes_file_upvotes.py
│ │ ├── 0008_userreport.py
│ │ ├── 0009_add_course_data.py
│ │ ├── 0010_add_professors.py
│ │ ├── 0011_auto_20240421_0406.py
│ │ ├── 0012_auto_20240422_1349.py
│ │ └── __init__.py
│ ├── models.py
│ ├── serializers.py
│ ├── tests.py
│ ├── urls.py
│ ├── user_login.py
│ ├── utils
│ │ ├── JSON_inputs
│ │ │ ├── courses.json
│ │ │ └── faculty_list.json
│ │ ├── __init__.py
│ │ └── get_client_ip.py
│ ├── views.py
│ └── views
│ │ └── filters.py
├── manage.py
├── peer_notes
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ ├── views.py
│ └── wsgi.py
├── requirements.txt
└── server.py
├── file_peer
├── .gitignore
├── peer_service.py
└── uploads
│ ├── 1_abcd.txt
│ └── 2_bcdef.txt
├── frontend
├── .eslintrc.cjs
├── .gitignore
├── README.md
├── index.html
├── package-lock.json
├── package.json
├── public
│ ├── PeerNotes.png
│ └── vite.svg
├── src
│ ├── App.tsx
│ ├── assets
│ │ ├── file.svg
│ │ └── react.svg
│ ├── contexts
│ │ └── session.ts
│ ├── hooks
│ │ ├── sessionRedirect.ts
│ │ └── usePoll.ts
│ ├── index.css
│ ├── main.tsx
│ ├── screens
│ │ ├── Login.tsx
│ │ ├── MainSearch.tsx
│ │ ├── RegisterFile.tsx
│ │ ├── Results.tsx
│ │ └── Signup.tsx
│ ├── styles
│ │ ├── App.css
│ │ ├── Login.css
│ │ ├── MainScreenWrapper.css
│ │ ├── RegisterFile.css
│ │ └── Results.module.css
│ ├── types
│ │ └── types.ts
│ ├── utils
│ │ └── getAuthHeaders.ts
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
└── test1.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # poetry
98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99 | # This is especially recommended for binary packages to ensure reproducibility, and is more
100 | # commonly ignored for libraries.
101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102 | #poetry.lock
103 |
104 | # pdm
105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106 | #pdm.lock
107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108 | # in version control.
109 | # https://pdm.fming.dev/#use-with-ide
110 | .pdm.toml
111 |
112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113 | __pypackages__/
114 |
115 | # Celery stuff
116 | celerybeat-schedule
117 | celerybeat.pid
118 |
119 | # SageMath parsed files
120 | *.sage.py
121 |
122 | # Environments
123 | .env
124 | .venv
125 | env/
126 | venv/
127 | ENV/
128 | env.bak/
129 | venv.bak/
130 |
131 | # Spyder project settings
132 | .spyderproject
133 | .spyproject
134 |
135 | # Rope project settings
136 | .ropeproject
137 |
138 | # mkdocs documentation
139 | /site
140 |
141 | # mypy
142 | .mypy_cache/
143 | .dmypy.json
144 | dmypy.json
145 |
146 | # Pyre type checker
147 | .pyre/
148 |
149 | # pytype static type analyzer
150 | .pytype/
151 |
152 | # Cython debug symbols
153 | cython_debug/
154 |
155 | # PyCharm
156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158 | # and can be added to the global gitignore or merged into this file. For a more nuclear
159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160 | #.idea/
161 |
162 | # Misc
163 | .DS_STORE
164 |
--------------------------------------------------------------------------------
/DummyFiles/CN-SDN.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmanTahiliani/PeerNotes/46a4574f62d46a0110f9324f7d0e23b3c8f3f9bc/DummyFiles/CN-SDN.pdf
--------------------------------------------------------------------------------
/DummyFiles/GradAlgoNotes.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmanTahiliani/PeerNotes/46a4574f62d46a0110f9324f7d0e23b3c8f3f9bc/DummyFiles/GradAlgoNotes.pdf
--------------------------------------------------------------------------------
/DummyFiles/IntroToMachineLearning.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmanTahiliani/PeerNotes/46a4574f62d46a0110f9324f7d0e23b3c8f3f9bc/DummyFiles/IntroToMachineLearning.pdf
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PeerNotes (In Progress)
2 | Repository for a course project of CS4675/CS6675 at Georgia Institute of Technology.
3 |
4 | ## Advisory for Contributors
5 |
6 | Please do not commit any code to the main branch directly
7 | Use feature branches, ideally with your name and the feature it is for mentioned in the branch name eg. feature_aman_login
8 | Raise a PR once you are ready and have checked your code for errors.
9 | Mention a bullet point summary for all the features you are pushing as part of the PR within the description to ease the review process
10 |
11 |
12 | ## Contributing to the Codebase
13 | 1. Create a new branch:
14 | ```bash
15 | git checkout -b
16 | ```
17 | 2. Make your changes.
18 |
19 | 3. Use black to format your code: (With virtual environment activated)
20 | ```bash
21 | black .
22 | ```
23 |
24 | 4. Make your changes and commit them:
25 | ```bash
26 | git add .
27 | git commit -m "Your commit message"
28 | ```
29 | 5. Push your changes to the remote repository:
30 | ```bash
31 | git push origin
32 | ```
33 | 6. Create a Pull Request on GitHub.
34 |
35 |
36 | ## Getting Started
37 |
38 | # Frontend (ReactJs)
39 | For our frontend we are using React + Vite + TypeScript!
40 |
41 | - **React** -> JavaScript Framework for creating reactive user interfaces.
42 | - **Vite** -> Development environment / build tool. Gives us access to features like hot reload, bundling, and plugins.
43 | - **TypeScript** -> A superset of JavaScript allowing for static types.
44 |
45 | Here's how to set up + run the frontend environment:
46 | 1. Download and install [Node.js](https://nodejs.org/en/download) v18+
47 | Check your node version:
48 | ```sh
49 | node -v
50 | ```
51 | 2. Clone the repo using Git
52 | 3. Install dependencies
53 | ```sh
54 | cd frontend
55 | npm install
56 | ```
57 | 4. Start the development server
58 | ```sh
59 | npm run dev
60 | ```
61 | 5. Visit http://localhost:5173
62 |
63 |
64 | # Backend
65 | # Central Server (Django)
66 |
67 | This is the backend for the Django project.
68 |
69 |
70 | ## Setup
71 |
72 | 1. Open a new terminal and cd into the backend directory:
73 | ```bash
74 | cd backend
75 | ```
76 |
77 | 2. Create a virtual environment:
78 | ```bash
79 | python3 -m venv venv
80 | ```
81 |
82 | 3. Activate the virtual environment:
83 | - For macOS/Linux:
84 | ```bash
85 | source venv/bin/activate
86 | ```
87 | - For Windows:
88 | ```bash
89 | venv\Scripts\activate
90 | ```
91 |
92 | 4. Install the project dependencies:
93 | ```bash
94 | pip install -r requirements.txt
95 | ```
96 |
97 | ### Running the Server
98 |
99 | To start the Django server, run the following command:
100 | ```bash
101 | python3 manage.py runserver
102 | ```
103 |
104 | By default, the server will run on `http://localhost:8000/`.
105 |
106 | ### Admin Panel
107 | In order to access the admin panel, you need to create a superuser. To do this, run the following command:
108 | ```bash
109 | python3 manage.py createsuperuser
110 | ```
111 |
112 | Then, you can access the admin panel by visiting `http://localhost:8000/admin/` and logging in with the superuser credentials.
113 |
114 | # Intermediate Proxy Service (Flask)
115 |
116 | This is the service which runs on the client side and is responsible for handling the peer to peer connections and file transfers along with getting the IPs of the machine. In order to run this, please do the following:
117 |
118 | 1. Open a new terminal and cd into the file_peer directory:
119 | ```bash
120 | cd file_peer
121 | ```
122 | 2. Make sure that the venv that we used for the backend service is activated
123 |
124 | 3. Run the flask server using the following command:
125 | ```bash
126 | python3 peer_service.py
127 | ```
128 |
129 |
130 |
131 |
132 | ## Further Configurations:
133 | - You would need to set up the central server with a domain and change the domain within the client code. (It is currently hardcoded with IP.)
134 | - Make sure all the servers are running within the same network as this solution is featured to be restricted to within a network by design.
--------------------------------------------------------------------------------
/backend/README.md:
--------------------------------------------------------------------------------
1 | # Django Backend
2 |
3 | This is the backend for the Django project.
4 |
5 | ## Setup
6 |
7 | 1. Create a virtual environment:
8 | ```bash
9 | python3 -m venv venv
10 | ```
11 |
12 | 2. Activate the virtual environment:
13 | - For macOS/Linux:
14 | ```bash
15 | source venv/bin/activate
16 | ```
17 | - For Windows:
18 | ```bash
19 | venv\Scripts\activate
20 | ```
21 |
22 | 3. Install the project dependencies:
23 | ```bash
24 | pip install -r requirements.txt
25 | ```
26 |
27 | ## Running the Server
28 |
29 | To start the Django server, run the following command:
30 | ```bash
31 | python3 manage.py runserver
32 | ```
33 |
34 | By default, the server will run on `http://localhost:8000/`.
35 |
36 | ## Admin Panel
37 | In order to access the admin panel, you need to create a superuser. To do this, run the following command:
38 | ```bash
39 | python3 manage.py createsuperuser
40 | ```
41 |
42 | Then, you can access the admin panel by visiting `http://localhost:8000/admin/` and logging in with the superuser credentials.
43 |
44 | ## Contributing to the Codebase
45 | 1. Create a new branch:
46 | ```bash
47 | git checkout -b
48 | ```
49 | 2. Make your changes.
50 |
51 | 3. Use black to format your code:
52 | ```bash
53 | black .
54 | ```
55 |
56 | 4. Make your changes and commit them:
57 | ```bash
58 | git add .
59 | git commit -m "Your commit message"
60 | ```
61 | 5. Push your changes to the remote repository:
62 | ```bash
63 | git push origin
64 | ```
65 | 6. Create a Pull Request on GitHub.
66 |
67 |
68 |
--------------------------------------------------------------------------------
/backend/api/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmanTahiliani/PeerNotes/46a4574f62d46a0110f9324f7d0e23b3c8f3f9bc/backend/api/__init__.py
--------------------------------------------------------------------------------
/backend/api/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from api.models import PeerUser, Semester, Professor, Course, Topic, File
3 |
4 | # Register your models here.
5 | admin.site.register(PeerUser)
6 | admin.site.register(Topic)
7 | admin.site.register(Professor)
8 | admin.site.register(Course)
9 | admin.site.register(File)
10 | admin.site.register(Semester)
11 |
--------------------------------------------------------------------------------
/backend/api/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class ApiConfig(AppConfig):
5 | default_auto_field = "django.db.models.BigAutoField"
6 | name = "api"
7 |
--------------------------------------------------------------------------------
/backend/api/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.11 on 2024-03-25 17:37
2 |
3 | import django.contrib.auth.models
4 | import django.contrib.auth.validators
5 | from django.db import migrations, models
6 | import django.utils.timezone
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | initial = True
12 |
13 | dependencies = [
14 | ("auth", "0012_alter_user_first_name_max_length"),
15 | ]
16 |
17 | operations = [
18 | migrations.CreateModel(
19 | name="PeerUser",
20 | fields=[
21 | (
22 | "id",
23 | models.BigAutoField(
24 | auto_created=True,
25 | primary_key=True,
26 | serialize=False,
27 | verbose_name="ID",
28 | ),
29 | ),
30 | ("password", models.CharField(max_length=128, verbose_name="password")),
31 | (
32 | "last_login",
33 | models.DateTimeField(
34 | blank=True, null=True, verbose_name="last login"
35 | ),
36 | ),
37 | (
38 | "is_superuser",
39 | models.BooleanField(
40 | default=False,
41 | help_text="Designates that this user has all permissions without explicitly assigning them.",
42 | verbose_name="superuser status",
43 | ),
44 | ),
45 | (
46 | "username",
47 | models.CharField(
48 | error_messages={
49 | "unique": "A user with that username already exists."
50 | },
51 | help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
52 | max_length=150,
53 | unique=True,
54 | validators=[
55 | django.contrib.auth.validators.UnicodeUsernameValidator()
56 | ],
57 | verbose_name="username",
58 | ),
59 | ),
60 | (
61 | "first_name",
62 | models.CharField(
63 | blank=True, max_length=150, verbose_name="first name"
64 | ),
65 | ),
66 | (
67 | "last_name",
68 | models.CharField(
69 | blank=True, max_length=150, verbose_name="last name"
70 | ),
71 | ),
72 | (
73 | "email",
74 | models.EmailField(
75 | blank=True, max_length=254, verbose_name="email address"
76 | ),
77 | ),
78 | (
79 | "is_staff",
80 | models.BooleanField(
81 | default=False,
82 | help_text="Designates whether the user can log into this admin site.",
83 | verbose_name="staff status",
84 | ),
85 | ),
86 | (
87 | "is_active",
88 | models.BooleanField(
89 | default=True,
90 | help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
91 | verbose_name="active",
92 | ),
93 | ),
94 | (
95 | "date_joined",
96 | models.DateTimeField(
97 | default=django.utils.timezone.now, verbose_name="date joined"
98 | ),
99 | ),
100 | ("ip_address", models.GenericIPAddressField(blank=True, null=True)),
101 | (
102 | "groups",
103 | models.ManyToManyField(
104 | blank=True,
105 | help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
106 | related_name="user_set",
107 | related_query_name="user",
108 | to="auth.group",
109 | verbose_name="groups",
110 | ),
111 | ),
112 | (
113 | "user_permissions",
114 | models.ManyToManyField(
115 | blank=True,
116 | help_text="Specific permissions for this user.",
117 | related_name="user_set",
118 | related_query_name="user",
119 | to="auth.permission",
120 | verbose_name="user permissions",
121 | ),
122 | ),
123 | ],
124 | options={
125 | "verbose_name": "user",
126 | "verbose_name_plural": "users",
127 | "abstract": False,
128 | },
129 | managers=[
130 | ("objects", django.contrib.auth.models.UserManager()),
131 | ],
132 | ),
133 | ]
134 |
--------------------------------------------------------------------------------
/backend/api/migrations/0002_course_professor_semester_topic_peeruser_last_poll_and_more.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.11 on 2024-03-26 04:40
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 | import django.utils.timezone
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ("api", "0001_initial"),
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name="Course",
18 | fields=[
19 | (
20 | "id",
21 | models.BigAutoField(
22 | auto_created=True,
23 | primary_key=True,
24 | serialize=False,
25 | verbose_name="ID",
26 | ),
27 | ),
28 | ("name", models.CharField(max_length=20)),
29 | ],
30 | ),
31 | migrations.CreateModel(
32 | name="Professor",
33 | fields=[
34 | (
35 | "id",
36 | models.BigAutoField(
37 | auto_created=True,
38 | primary_key=True,
39 | serialize=False,
40 | verbose_name="ID",
41 | ),
42 | ),
43 | ("name", models.CharField(max_length=100)),
44 | ],
45 | ),
46 | migrations.CreateModel(
47 | name="Semester",
48 | fields=[
49 | (
50 | "id",
51 | models.BigAutoField(
52 | auto_created=True,
53 | primary_key=True,
54 | serialize=False,
55 | verbose_name="ID",
56 | ),
57 | ),
58 | ("name", models.CharField(max_length=20)),
59 | ],
60 | ),
61 | migrations.CreateModel(
62 | name="Topic",
63 | fields=[
64 | (
65 | "id",
66 | models.BigAutoField(
67 | auto_created=True,
68 | primary_key=True,
69 | serialize=False,
70 | verbose_name="ID",
71 | ),
72 | ),
73 | ("name", models.CharField(max_length=100)),
74 | ("description", models.TextField(blank=True)),
75 | ],
76 | ),
77 | migrations.AddField(
78 | model_name="peeruser",
79 | name="last_poll",
80 | field=models.DateTimeField(
81 | auto_now_add=True, default=django.utils.timezone.now
82 | ),
83 | preserve_default=False,
84 | ),
85 | migrations.CreateModel(
86 | name="File",
87 | fields=[
88 | (
89 | "id",
90 | models.BigAutoField(
91 | auto_created=True,
92 | primary_key=True,
93 | serialize=False,
94 | verbose_name="ID",
95 | ),
96 | ),
97 | ("filename", models.CharField(max_length=200)),
98 | (
99 | "original_author",
100 | models.ForeignKey(
101 | null=True,
102 | on_delete=django.db.models.deletion.SET_NULL,
103 | to=settings.AUTH_USER_MODEL,
104 | ),
105 | ),
106 | (
107 | "peer_users",
108 | models.ManyToManyField(
109 | related_name="shared_files", to=settings.AUTH_USER_MODEL
110 | ),
111 | ),
112 | (
113 | "professor",
114 | models.ForeignKey(
115 | null=True,
116 | on_delete=django.db.models.deletion.SET_NULL,
117 | to="api.professor",
118 | ),
119 | ),
120 | (
121 | "semester",
122 | models.ForeignKey(
123 | null=True,
124 | on_delete=django.db.models.deletion.SET_NULL,
125 | to="api.semester",
126 | ),
127 | ),
128 | (
129 | "topic",
130 | models.ForeignKey(
131 | null=True,
132 | on_delete=django.db.models.deletion.SET_NULL,
133 | to="api.topic",
134 | ),
135 | ),
136 | ],
137 | ),
138 | ]
139 |
--------------------------------------------------------------------------------
/backend/api/migrations/0003_file_course_alter_file_original_author_and_more.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.11 on 2024-03-26 16:30
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ("api", "0002_course_professor_semester_topic_peeruser_last_poll_and_more"),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name="file",
17 | name="course",
18 | field=models.ForeignKey(
19 | null=True,
20 | on_delete=django.db.models.deletion.SET_NULL,
21 | related_name="files",
22 | to="api.course",
23 | ),
24 | ),
25 | migrations.AlterField(
26 | model_name="file",
27 | name="original_author",
28 | field=models.ForeignKey(
29 | null=True,
30 | on_delete=django.db.models.deletion.SET_NULL,
31 | related_name="owned_files",
32 | to=settings.AUTH_USER_MODEL,
33 | ),
34 | ),
35 | migrations.AlterField(
36 | model_name="file",
37 | name="professor",
38 | field=models.ForeignKey(
39 | null=True,
40 | on_delete=django.db.models.deletion.SET_NULL,
41 | related_name="files",
42 | to="api.professor",
43 | ),
44 | ),
45 | migrations.AlterField(
46 | model_name="file",
47 | name="semester",
48 | field=models.ForeignKey(
49 | null=True,
50 | on_delete=django.db.models.deletion.SET_NULL,
51 | related_name="files",
52 | to="api.semester",
53 | ),
54 | ),
55 | migrations.AlterField(
56 | model_name="file",
57 | name="topic",
58 | field=models.ForeignKey(
59 | null=True,
60 | on_delete=django.db.models.deletion.SET_NULL,
61 | related_name="files",
62 | to="api.topic",
63 | ),
64 | ),
65 | ]
66 |
--------------------------------------------------------------------------------
/backend/api/migrations/0004_course_number_alter_course_name.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.11 on 2024-03-26 17:52
2 |
3 | from django.db import migrations, models
4 | import django.utils.timezone
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("api", "0003_file_course_alter_file_original_author_and_more"),
11 | ]
12 |
13 | operations = [
14 | migrations.AddField(
15 | model_name="course",
16 | name="number",
17 | field=models.CharField(
18 | default=django.utils.timezone.now, max_length=20, unique=True
19 | ),
20 | preserve_default=False,
21 | ),
22 | migrations.AlterField(
23 | model_name="course",
24 | name="name",
25 | field=models.CharField(blank=True, max_length=40),
26 | ),
27 | ]
28 |
--------------------------------------------------------------------------------
/backend/api/migrations/0005_file_created_at.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.11 on 2024-03-26 23:14
2 |
3 | from django.db import migrations, models
4 | import django.utils.timezone
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("api", "0004_course_number_alter_course_name"),
11 | ]
12 |
13 | operations = [
14 | migrations.AddField(
15 | model_name="file",
16 | name="created_at",
17 | field=models.DateTimeField(
18 | auto_now_add=True, default=django.utils.timezone.now
19 | ),
20 | preserve_default=False,
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/backend/api/migrations/0006_file_points_peeruser_points.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.11 on 2024-04-12 20:24
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("api", "0005_file_created_at"),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name="file",
15 | name="points",
16 | field=models.IntegerField(default=0),
17 | ),
18 | migrations.AddField(
19 | model_name="peeruser",
20 | name="points",
21 | field=models.IntegerField(default=0),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/backend/api/migrations/0007_remove_file_points_file_downvotes_file_upvotes.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.11 on 2024-04-12 21:47
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("api", "0006_file_points_peeruser_points"),
11 | ]
12 |
13 | operations = [
14 | migrations.RemoveField(
15 | model_name="file",
16 | name="points",
17 | ),
18 | migrations.AddField(
19 | model_name="file",
20 | name="downvotes",
21 | field=models.ManyToManyField(
22 | related_name="downvoted_file", to=settings.AUTH_USER_MODEL
23 | ),
24 | ),
25 | migrations.AddField(
26 | model_name="file",
27 | name="upvotes",
28 | field=models.ManyToManyField(
29 | related_name="upvoted_file", to=settings.AUTH_USER_MODEL
30 | ),
31 | ),
32 | ]
33 |
--------------------------------------------------------------------------------
/backend/api/migrations/0008_userreport.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.11 on 2024-04-20 23:10
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ("api", "0007_remove_file_points_file_downvotes_file_upvotes"),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name="UserReport",
17 | fields=[
18 | (
19 | "id",
20 | models.BigAutoField(
21 | auto_created=True,
22 | primary_key=True,
23 | serialize=False,
24 | verbose_name="ID",
25 | ),
26 | ),
27 | ("description", models.TextField(max_length=300)),
28 | (
29 | "file",
30 | models.ForeignKey(
31 | null=True,
32 | on_delete=django.db.models.deletion.CASCADE,
33 | related_name="file_report",
34 | to="api.file",
35 | ),
36 | ),
37 | (
38 | "reporting_user",
39 | models.ForeignKey(
40 | null=True,
41 | on_delete=django.db.models.deletion.SET_NULL,
42 | related_name="user_report_generated",
43 | to=settings.AUTH_USER_MODEL,
44 | ),
45 | ),
46 | (
47 | "user",
48 | models.ForeignKey(
49 | null=True,
50 | on_delete=django.db.models.deletion.SET_NULL,
51 | related_name="user_report",
52 | to=settings.AUTH_USER_MODEL,
53 | ),
54 | ),
55 | ],
56 | ),
57 | ]
58 |
--------------------------------------------------------------------------------
/backend/api/migrations/0009_add_course_data.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.11 on 2024-04-21 03:17
2 |
3 | from django.db import migrations
4 | import os
5 |
6 | import json
7 |
8 |
9 | def load_data_from_json(apps, schema_editor):
10 | Course = apps.get_model("api", "Course")
11 | json_file = os.path.abspath(
12 | os.path.join(
13 | os.path.dirname(__file__), "..", "utils", "JSON_inputs", "courses.json"
14 | )
15 | )
16 |
17 | with open(json_file, "r") as f:
18 | data = json.load(f)
19 |
20 | for item in data:
21 | try:
22 | Course.objects.create(
23 | name=item["name"],
24 | number=item["number"],
25 | )
26 | except Exception as e:
27 | print("Error:", str(e))
28 |
29 |
30 | class Migration(migrations.Migration):
31 |
32 | dependencies = [
33 | ("api", "0008_userreport"),
34 | ]
35 |
36 | operations = [
37 | migrations.RunPython(load_data_from_json),
38 | ]
39 |
--------------------------------------------------------------------------------
/backend/api/migrations/0010_add_professors.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.11 on 2024-04-21 04:01
2 |
3 | from django.db import migrations
4 | import os
5 |
6 | import json
7 |
8 |
9 | def load_data_from_json(apps, schema_editor):
10 | Professor = apps.get_model("api", "Professor")
11 | json_file = os.path.abspath(
12 | os.path.join(
13 | os.path.dirname(__file__), "..", "utils", "JSON_inputs", "faculty_list.json"
14 | )
15 | )
16 |
17 | with open(json_file, "r") as f:
18 | data = json.load(f)
19 |
20 | for item in data:
21 | try:
22 | Professor.objects.create(
23 | name=item["name"],
24 | )
25 | except Exception as e:
26 | print("Error:", str(e))
27 |
28 |
29 | class Migration(migrations.Migration):
30 |
31 | dependencies = [
32 | ("api", "0009_add_course_data"),
33 | ]
34 |
35 | operations = [
36 | migrations.RunPython(load_data_from_json),
37 | ]
38 |
--------------------------------------------------------------------------------
/backend/api/migrations/0011_auto_20240421_0406.py:
--------------------------------------------------------------------------------
1 | from django.db import migrations
2 | import os
3 |
4 | import json
5 |
6 |
7 | def load_data_from_json(apps, schema_editor):
8 | Semester = apps.get_model("api", "Semester")
9 | Semester.objects.all().delete()
10 | for year in range(2015, 2024):
11 | for sem in ["Spring", "Summer", "Fall"]:
12 | try:
13 | Semester.objects.create(
14 | name=f"{sem} {year}",
15 | )
16 | except Exception as e:
17 | print("Error:", str(e))
18 |
19 |
20 | class Migration(migrations.Migration):
21 |
22 | dependencies = [
23 | ("api", "0010_add_professors"),
24 | ]
25 | operations = [
26 | migrations.RunPython(load_data_from_json),
27 | ]
28 |
--------------------------------------------------------------------------------
/backend/api/migrations/0012_auto_20240422_1349.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.11 on 2024-04-22 13:49
2 |
3 | from django.db import migrations
4 |
5 |
6 | def load_data_from_json(apps, schema_editor):
7 | topics = [
8 | "Introduction to Computer Science",
9 | "Programming Fundamentals",
10 | "Data Structures",
11 | "Algorithms",
12 | "Computer Architecture",
13 | "Operating Systems",
14 | "Database Systems",
15 | "Networking",
16 | "Software Engineering",
17 | "Artificial Intelligence",
18 | "Theory of Computation",
19 | "Compiler Design",
20 | "Distributed Systems",
21 | "Computer Graphics",
22 | "Cryptography",
23 | "Parallel and Concurrent Programming",
24 | "Human-Computer Interaction",
25 | "Computer Vision",
26 | "Web Technologies",
27 | "Embedded Systems",
28 | "Data Mining and Analytics",
29 | "Software Quality Assurance",
30 | "Ethical and Social Implications of Computing",
31 | "Advanced Topics",
32 | "Automata Theory",
33 | "Formal Languages and Automata",
34 | "Computational Complexity",
35 | "Graph Theory",
36 | "Computational Geometry",
37 | "Numerical Analysis",
38 | "Machine Learning",
39 | "Deep Learning",
40 | "Natural Language Processing",
41 | "Reinforcement Learning",
42 | "Computer Security",
43 | "Network Security",
44 | "Information Retrieval",
45 | "Cloud Computing",
46 | "IoT (Internet of Things)",
47 | "Virtual Reality",
48 | "Augmented Reality",
49 | "Mobile Application Development",
50 | "Game Development",
51 | "Computer Animation",
52 | "Operating System Design",
53 | "Real-Time Systems",
54 | "Concurrency Control",
55 | "Transaction Processing",
56 | "Data Warehousing",
57 | "Big Data Technologies",
58 | "Data Science",
59 | "Business Intelligence",
60 | "Quantum Computing",
61 | "Bioinformatics",
62 | "Computational Biology",
63 | "Robotics",
64 | "Computer Ethics",
65 | "Privacy and Security in Computing",
66 | "Digital Forensics",
67 | "Cryptocurrency and Blockchain",
68 | "Social Network Analysis",
69 | "Cloud Security",
70 | "Edge Computing",
71 | "Compiler Optimization",
72 | "High-Performance Computing",
73 | "Grid Computing",
74 | "Fuzzy Logic and Systems",
75 | "Expert Systems",
76 | "Pattern Recognition",
77 | "Knowledge Representation and Reasoning",
78 | "Semantic Web",
79 | "Computer Algebra Systems",
80 | "Geographic Information Systems",
81 | "Wireless Sensor Networks",
82 | "Biometric Authentication",
83 | "Internet Security Protocols",
84 | "Software Testing and Debugging",
85 | "Agile Methodologies",
86 | "DevOps",
87 | "Continuous Integration and Continuous Deployment (CI/CD)",
88 | "Model-Driven Engineering",
89 | "User Interface Design",
90 | "Accessibility in Computing",
91 | "User Experience (UX) Design",
92 | "Game Theory",
93 | "Social Computing",
94 | "Computational Sociology",
95 | "Computational Linguistics",
96 | "Music Information Retrieval",
97 | "Ethics in Artificial Intelligence",
98 | "Algorithmic Game Theory",
99 | "Behavioral Economics and Computing",
100 | "Neuroinformatics",
101 | "Health Informatics",
102 | "Geometric Modeling",
103 | "Scientific Computing",
104 | "Information Theory",
105 | "System Modeling and Simulation",
106 | "Decision Support Systems",
107 | "Database Security and Privacy",
108 | "Knowledge Management Systems",
109 | "Information Visualization",
110 | "Geospatial Data Analysis",
111 | "Enterprise Architecture",
112 | "Supply Chain Management Systems",
113 | "Data Integration and Interoperability",
114 | "Software Reuse and Component-Based Development",
115 | "Legal and Regulatory Issues in Computing",
116 | "Information Assurance",
117 | "Systems Analysis and Design",
118 | "E-commerce Systems",
119 | "E-learning Systems",
120 | "Human-Robot Interaction",
121 | "Biologically Inspired Computing",
122 | "Social Robotics",
123 | "Humanoid Robotics",
124 | "Computational Neuroscience",
125 | "Affective Computing",
126 | "Explainable AI",
127 | "Robot Ethics",
128 | "Cyber-Physical Systems",
129 | "Internet of Medical Things (IoMT)",
130 | "Quantum Information Science",
131 | "Quantum Cryptography",
132 | "Quantum Algorithms",
133 | "Quantum Machine Learning",
134 | "Quantum Error Correction",
135 | "Quantum Networking",
136 | ]
137 |
138 | Topic = apps.get_model("api", "Topic")
139 | Topic.objects.all().delete()
140 | for topic in topics:
141 | try:
142 | Topic.objects.create(
143 | name=topic,
144 | )
145 | except Exception as e:
146 | print("Error:", str(e))
147 |
148 |
149 | class Migration(migrations.Migration):
150 |
151 | dependencies = [
152 | ("api", "0011_auto_20240421_0406"),
153 | ]
154 |
155 | operations = [
156 | migrations.RunPython(load_data_from_json),
157 | ]
158 |
--------------------------------------------------------------------------------
/backend/api/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmanTahiliani/PeerNotes/46a4574f62d46a0110f9324f7d0e23b3c8f3f9bc/backend/api/migrations/__init__.py
--------------------------------------------------------------------------------
/backend/api/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.auth.models import AbstractUser
3 | from django.db import models
4 |
5 |
6 | class PeerUser(AbstractUser):
7 | ip_address = models.GenericIPAddressField(blank=True, null=True)
8 | last_poll = models.DateTimeField(auto_now_add=True)
9 | points = models.IntegerField(default=0, blank=False)
10 |
11 |
12 | class Topic(models.Model):
13 | name = models.CharField(max_length=100, blank=False, null=False)
14 | description = models.TextField(blank=True)
15 |
16 | def __str__(self) -> str:
17 | return self.name
18 |
19 |
20 | class Professor(models.Model):
21 | name = models.CharField(max_length=100, blank=False, null=False)
22 |
23 | def __str__(self) -> str:
24 | return self.name
25 |
26 |
27 | class Semester(models.Model):
28 | name = models.CharField(max_length=20, blank=False, null=False)
29 |
30 | def __str__(self) -> str:
31 | return self.name
32 |
33 |
34 | class Course(models.Model):
35 | name = models.CharField(max_length=40, blank=True)
36 | number = models.CharField(max_length=20, blank=False, null=False, unique=True)
37 |
38 | def __str__(self) -> str:
39 | return self.number + f" ({self.name})"
40 |
41 |
42 | class File(models.Model):
43 | filename = models.CharField(max_length=200, blank=False, null=False)
44 | original_author = models.ForeignKey(
45 | PeerUser, on_delete=models.SET_NULL, null=True, related_name="owned_files"
46 | )
47 | peer_users = models.ManyToManyField(PeerUser, related_name="shared_files")
48 | topic = models.ForeignKey(
49 | Topic, on_delete=models.SET_NULL, null=True, related_name="files"
50 | )
51 | professor = models.ForeignKey(
52 | Professor, on_delete=models.SET_NULL, null=True, related_name="files"
53 | )
54 | semester = models.ForeignKey(
55 | Semester, on_delete=models.SET_NULL, null=True, related_name="files"
56 | )
57 | course = models.ForeignKey(
58 | Course, on_delete=models.SET_NULL, null=True, related_name="files"
59 | )
60 | created_at = models.DateTimeField(auto_now_add=True)
61 | upvotes = models.ManyToManyField(PeerUser, related_name="upvoted_file")
62 | downvotes = models.ManyToManyField(PeerUser, related_name="downvoted_file")
63 |
64 | @property
65 | def points(self):
66 | return self.upvotes.count() - self.downvotes.count()
67 |
68 | def __str__(self):
69 | return self.filename
70 |
71 |
72 | class UserReport(models.Model):
73 | user = models.ForeignKey(
74 | PeerUser, on_delete=models.SET_NULL, null=True, related_name="user_report"
75 | )
76 | reporting_user = models.ForeignKey(
77 | PeerUser,
78 | on_delete=models.SET_NULL,
79 | null=True,
80 | related_name="user_report_generated",
81 | )
82 | description = models.TextField(blank=False, null=False, max_length=300)
83 | file = models.ForeignKey(
84 | File, on_delete=models.CASCADE, null=True, related_name="file_report"
85 | )
86 |
87 | def __str__(self) -> str:
88 | return f"{self.user}:{self.file}:{self.reporting_user}"
89 |
--------------------------------------------------------------------------------
/backend/api/serializers.py:
--------------------------------------------------------------------------------
1 | from rest_framework import serializers
2 | from .models import PeerUser, Topic, Semester, Professor, Course, File
3 |
4 |
5 | class LoginSerializer(serializers.Serializer):
6 | username = serializers.CharField()
7 | password = serializers.CharField()
8 |
9 |
10 | class UserSerializer(serializers.ModelSerializer):
11 | class Meta:
12 | model = PeerUser
13 | fields = ["id", "username", "email", "ip_address", "points"]
14 |
15 |
16 | class TopicSerializer(serializers.ModelSerializer):
17 | class Meta:
18 | model = Topic
19 | fields = "__all__"
20 |
21 |
22 | class ProfessorSerializer(serializers.ModelSerializer):
23 | class Meta:
24 | model = Professor
25 | fields = "__all__"
26 |
27 |
28 | class SemesterSerializer(serializers.ModelSerializer):
29 | class Meta:
30 | model = Semester
31 | fields = "__all__"
32 |
33 |
34 | class CourseSerializer(serializers.ModelSerializer):
35 | class Meta:
36 | model = Course
37 | fields = "__all__"
38 |
39 |
40 | class FileSerializer(serializers.ModelSerializer):
41 | original_author = UserSerializer()
42 | peer_users = UserSerializer(many=True)
43 | topic = TopicSerializer()
44 | professor = ProfessorSerializer()
45 | semester = SemesterSerializer()
46 | course = CourseSerializer()
47 |
48 | class Meta:
49 | model = File
50 | fields = [
51 | "id",
52 | "filename",
53 | "points",
54 | "original_author",
55 | "peer_users",
56 | "topic",
57 | "professor",
58 | "semester",
59 | "course",
60 | "created_at",
61 | "upvotes",
62 | "downvotes",
63 | ]
64 |
--------------------------------------------------------------------------------
/backend/api/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/backend/api/urls.py:
--------------------------------------------------------------------------------
1 | """
2 | URL configuration for peer_notes project.
3 |
4 | The `urlpatterns` list routes URLs to views. For more information please see:
5 | https://docs.djangoproject.com/en/4.2/topics/http/urls/
6 | Examples:
7 | Function views
8 | 1. Add an import: from my_app import views
9 | 2. Add a URL to urlpatterns: path('', views.home, name='home')
10 | Class-based views
11 | 1. Add an import: from other_app.views import Home
12 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
13 | Including another URLconf
14 | 1. Import the include() function: from django.urls import include, path
15 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
16 | """
17 |
18 | from django.urls import path
19 | from api.user_login import LoginView, SignupView, PollOnlineView
20 | from api import views
21 |
22 | urlpatterns = [
23 | path("api/login/", LoginView.as_view(), name="login"),
24 | path("api/signup/", SignupView.as_view(), name="signup"),
25 | path("api/poll/", PollOnlineView.as_view(), name="poll"),
26 | path("api/topics/", views.TopicListCreateAPIView.as_view(), name="topic-list"),
27 | path(
28 | "api/topics//", views.TopicDetailAPIView.as_view(), name="topic-detail"
29 | ),
30 | path(
31 | "api/professors/",
32 | views.ProfessorListCreateAPIView.as_view(),
33 | name="professor-list",
34 | ),
35 | path(
36 | "api/professors//",
37 | views.ProfessorDetailAPIView.as_view(),
38 | name="professor-detail",
39 | ),
40 | path(
41 | "api/semesters/",
42 | views.SemesterListCreateAPIView.as_view(),
43 | name="semester-list",
44 | ),
45 | path(
46 | "api/semesters//",
47 | views.SemesterDetailAPIView.as_view(),
48 | name="semester-detail",
49 | ),
50 | path("api/courses/", views.CourseListCreateAPIView.as_view(), name="course-list"),
51 | path(
52 | "api/courses//",
53 | views.CourseDetailAPIView.as_view(),
54 | name="course-detail",
55 | ),
56 | path("api/files/", views.FileListCreateAPIView.as_view(), name="file-list"),
57 | path(
58 | "api/files//upvote/",
59 | views.UpvoteFile.as_view(),
60 | name="upvote-file",
61 | ),
62 | path(
63 | "api/files//downvote/",
64 | views.DownvoteFile.as_view(),
65 | name="downvote-file",
66 | ),
67 | path(
68 | "api/files//add-peer/",
69 | views.AddPeerToFile.as_view(),
70 | name="add-peer",
71 | ),
72 | path("api/files//", views.FileDetailAPIView.as_view(), name="file-detail"),
73 | path("api/report_user/", views.ReportUserView.as_view(), name="report-user"),
74 | path("api/register/", views.RegisterFile.as_view(), name="file-register"),
75 | path("api/files/filter/", views.FileFilterView.as_view(), name="file-filter-view"),
76 | path("api/get-peer-files/", views.UserFilesView.as_view(), name="user-files-view"),
77 | ]
78 |
--------------------------------------------------------------------------------
/backend/api/user_login.py:
--------------------------------------------------------------------------------
1 | from api.serializers import LoginSerializer, UserSerializer
2 | from django.contrib.auth import authenticate, get_user_model
3 | from rest_framework import status
4 | from rest_framework.authtoken.models import Token
5 | from rest_framework.response import Response
6 | from rest_framework.views import APIView
7 | from rest_framework.authentication import TokenAuthentication
8 | from rest_framework.permissions import IsAuthenticated
9 | from api.utils.get_client_ip import get_client_ip, PollMiddleware
10 | from django.utils import timezone
11 |
12 |
13 | class LoginView(APIView):
14 | def post(self, request):
15 | serializer = LoginSerializer(data=request.data)
16 | if serializer.is_valid():
17 | username = serializer.validated_data["username"]
18 | password = serializer.validated_data["password"]
19 | user = authenticate(username=username, password=password)
20 | if user:
21 | token, _ = Token.objects.get_or_create(user=user)
22 | user.last_poll = timezone.now()
23 | response = Response({"token": token.key}, status=status.HTTP_200_OK)
24 | response.set_cookie("token", token.key)
25 | return response
26 | else:
27 | return Response(
28 | {"error": "Invalid credentials"},
29 | status=status.HTTP_401_UNAUTHORIZED,
30 | )
31 | else:
32 | return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
33 |
34 |
35 | class SignupView(APIView):
36 | def post(self, request):
37 | serializer = UserSerializer(data=request.data)
38 | if serializer.is_valid():
39 | print(serializer.validated_data)
40 | username = serializer.validated_data["username"]
41 | email = serializer.validated_data["email"]
42 | password = request.data["password"]
43 | ip_address = get_client_ip(request)
44 | print(email, username, password)
45 | if get_user_model().objects.filter(username=username).exists():
46 | return Response(
47 | {"error": "Username already exists"},
48 | status=status.HTTP_400_BAD_REQUEST,
49 | )
50 |
51 | user = get_user_model().objects.create_user(
52 | username=username, email=email, password=password, ip_address=ip_address
53 | )
54 |
55 | return Response(
56 | {"message": "User created successfully"}, status=status.HTTP_201_CREATED
57 | )
58 | else:
59 | return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
60 |
61 |
62 | class PollOnlineView(APIView):
63 | authentication_classes = [TokenAuthentication]
64 | permission_classes = [IsAuthenticated]
65 |
66 | def post(self, request):
67 | try:
68 | user = request.user
69 | try:
70 | data = request.data
71 | ip_address = data["ip"]
72 | print("Local IP found in request")
73 | except Exception as e:
74 | ip_address = get_client_ip(request)
75 | print("Using public IP")
76 | user.ip_address = ip_address
77 | user.last_poll = timezone.now()
78 | user.save()
79 | return Response(
80 | {
81 | "Message": "IP address updated successfully",
82 | "username": user.username,
83 | },
84 | status=status.HTTP_200_OK,
85 | )
86 | except Exception as e:
87 | return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
88 |
--------------------------------------------------------------------------------
/backend/api/utils/JSON_inputs/courses.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "number": "CS 1100",
4 | "name": "Freshman Leap Seminar"
5 | },
6 | {
7 | "number": "CS 1171",
8 | "name": "Computing in MATLAB"
9 | },
10 | {
11 | "number": "CS 1301",
12 | "name": "Intro to Computing"
13 | },
14 | {
15 | "number": "CS 1301R",
16 | "name": "CS 1301 Recitation"
17 | },
18 | {
19 | "number": "CS 1315",
20 | "name": "Intro Media Computation"
21 | },
22 | {
23 | "number": "CS 1315R",
24 | "name": "CS 1315 Recitation"
25 | },
26 | {
27 | "number": "CS 1316",
28 | "name": "Rep Structure & Behavior"
29 | },
30 | {
31 | "number": "CS 1331",
32 | "name": "Intro-Object Orient Prog"
33 | },
34 | {
35 | "number": "CS 1331R",
36 | "name": "CS 1331 Recitation"
37 | },
38 | {
39 | "number": "CS 1332",
40 | "name": "Data Struct & Algorithms"
41 | },
42 | {
43 | "number": "CS 1332R",
44 | "name": "CS 1332 Recitation"
45 | },
46 | {
47 | "number": "CS 1371",
48 | "name": "Computing for Engineers"
49 | },
50 | {
51 | "number": "CS 1371R",
52 | "name": "CS 1371 Recitation"
53 | },
54 | {
55 | "number": "CS 1372",
56 | "name": "Program Design for Engrs"
57 | },
58 | {
59 | "number": "CS 1801",
60 | "name": "Special Topics"
61 | },
62 | {
63 | "number": "CS 1802",
64 | "name": "Special Topics"
65 | },
66 | {
67 | "number": "CS 1803",
68 | "name": "Special Topics"
69 | },
70 | {
71 | "number": "CS 1804",
72 | "name": "Special Topics"
73 | },
74 | {
75 | "number": "CS 1805",
76 | "name": "Special Topics"
77 | },
78 | {
79 | "number": "CS 1XXX",
80 | "name": "Computer Sci Elective"
81 | },
82 | {
83 | "number": "CS 2050",
84 | "name": "Intro Discrete Math CS"
85 | },
86 | {
87 | "number": "CS 2050R",
88 | "name": "CS 2050 Recitation"
89 | },
90 | {
91 | "number": "CS 2051",
92 | "name": "Honors Discrete Math CS"
93 | },
94 | {
95 | "number": "CS 2110",
96 | "name": "Computer Organiz&Program"
97 | },
98 | {
99 | "number": "CS 2200",
100 | "name": "Systems and Networks"
101 | },
102 | {
103 | "number": "CS 2261",
104 | "name": "Media Device Architectur"
105 | },
106 | {
107 | "number": "CS 2316",
108 | "name": "Data Input/Manipulation"
109 | },
110 | {
111 | "number": "CS 2316R",
112 | "name": "CS 2316 Recitation"
113 | },
114 | {
115 | "number": "CS 2335",
116 | "name": "Software Practicum"
117 | },
118 | {
119 | "number": "CS 2340",
120 | "name": "Objects and Design"
121 | },
122 | {
123 | "number": "CS 2345",
124 | "name": "Adv Practical O-O Prog"
125 | },
126 | {
127 | "number": "CS 2600",
128 | "name": "Knowledge Rep & Process"
129 | },
130 | {
131 | "number": "CS 2698",
132 | "name": "Research Assistantship"
133 | },
134 | {
135 | "number": "CS 2699",
136 | "name": "Undergraduate Research"
137 | },
138 | {
139 | "number": "CS 2701",
140 | "name": "Startup Lab"
141 | },
142 | {
143 | "number": "CS 2701R",
144 | "name": "CS 2701 Recitation"
145 | },
146 | {
147 | "number": "CS 2801",
148 | "name": "Special Topics"
149 | },
150 | {
151 | "number": "CS 2802",
152 | "name": "Special Topics"
153 | },
154 | {
155 | "number": "CS 2803",
156 | "name": "Special Topics"
157 | },
158 | {
159 | "number": "CS 2804",
160 | "name": "Special Topics"
161 | },
162 | {
163 | "number": "CS 2805",
164 | "name": "Special Topics"
165 | },
166 | {
167 | "number": "CS 2XXX",
168 | "name": "Computer Sci Elective"
169 | },
170 | {
171 | "number": "CS 3001",
172 | "name": "Computing & Society"
173 | },
174 | {
175 | "number": "CS 3101",
176 | "name": "Comp Sci Ventures"
177 | },
178 | {
179 | "number": "CS 3210",
180 | "name": "Design-Operating Systems"
181 | },
182 | {
183 | "number": "CS 3220",
184 | "name": "Processor Design"
185 | },
186 | {
187 | "number": "CS 3240",
188 | "name": "Languages and Computation"
189 | },
190 | {
191 | "number": "CS 3251",
192 | "name": "Computer Networking I"
193 | },
194 | {
195 | "number": "CS 3300",
196 | "name": "Intro to Software Engr"
197 | },
198 | {
199 | "number": "CS 3311",
200 | "name": "Project Design"
201 | },
202 | {
203 | "number": "CS 3312",
204 | "name": "Project Implementation"
205 | },
206 | {
207 | "number": "CS 3451",
208 | "name": "Computer Graphics"
209 | },
210 | {
211 | "number": "CS 3510",
212 | "name": "Dsgn&Analysis-Algorithms"
213 | },
214 | {
215 | "number": "CS 3511",
216 | "name": "Algorithms Honors"
217 | },
218 | {
219 | "number": "CS 3600",
220 | "name": "Intro-Artificial Intell"
221 | },
222 | {
223 | "number": "CS 3630",
224 | "name": "Intro-Perception&Robotic"
225 | },
226 | {
227 | "number": "CS 3651",
228 | "name": "Prototyping Intelligent Device"
229 | },
230 | {
231 | "number": "CS 3651R",
232 | "name": "CS 3651 Recitation"
233 | },
234 | {
235 | "number": "CS 3743",
236 | "name": "Emerging Technologies"
237 | },
238 | {
239 | "number": "CS 3744",
240 | "name": "Mangn Prod Serv Tech Dev"
241 | },
242 | {
243 | "number": "CS 3750",
244 | "name": "User Interface Design"
245 | },
246 | {
247 | "number": "CS 3751",
248 | "name": "Intro UI Design"
249 | },
250 | {
251 | "number": "CS 3790",
252 | "name": "Intro-Cognitive Science"
253 | },
254 | {
255 | "number": "CS 3801",
256 | "name": "Special Topics"
257 | },
258 | {
259 | "number": "CS 3802",
260 | "name": "Special Topics"
261 | },
262 | {
263 | "number": "CS 3803",
264 | "name": "Special Topics"
265 | },
266 | {
267 | "number": "CS 3804",
268 | "name": "Special Topics"
269 | },
270 | {
271 | "number": "CS 3805",
272 | "name": "Special Topics"
273 | },
274 | {
275 | "number": "CS 3873",
276 | "name": "Special Topics"
277 | },
278 | {
279 | "number": "CS 3XXX",
280 | "name": "Computer Sci"
281 | },
282 | {
283 | "number": "CS 4001",
284 | "name": "Computing & Society"
285 | },
286 | {
287 | "number": "CS 4002",
288 | "name": "Robots and Society"
289 | },
290 | {
291 | "number": "CS 4003",
292 | "name": "AI Ethics and Society"
293 | },
294 | {
295 | "number": "CS 4005",
296 | "name": "Next Gen Computing Tech"
297 | },
298 | {
299 | "number": "CS 4010",
300 | "name": "Intro to Computer Law"
301 | },
302 | {
303 | "number": "CS 4052",
304 | "name": "Systems Analysis& Design"
305 | },
306 | {
307 | "number": "CS 4057",
308 | "name": "Bus Process Analy&Design"
309 | },
310 | {
311 | "number": "CS 4117",
312 | "name": "Intro Malware Rev Eng"
313 | },
314 | {
315 | "number": "CS 4210",
316 | "name": "Adv Operating Systems"
317 | },
318 | {
319 | "number": "CS 4220",
320 | "name": "Embedded Systems"
321 | },
322 | {
323 | "number": "CS 4220R",
324 | "name": "CS 4220 Recitation"
325 | },
326 | {
327 | "number": "CS 4233",
328 | "name": "Parallel Comp Arch"
329 | },
330 | {
331 | "number": "CS 4235",
332 | "name": "Intro to Info Security"
333 | },
334 | {
335 | "number": "CS 4237",
336 | "name": "Comp & Network Security"
337 | },
338 | {
339 | "number": "CS 4238",
340 | "name": "Computer Sys Security"
341 | },
342 | {
343 | "number": "CS 4239",
344 | "name": "Enterprise Cyber Mgt"
345 | },
346 | {
347 | "number": "CS 4240",
348 | "name": "Compilers & Interpreters"
349 | },
350 | {
351 | "number": "CS 4245",
352 | "name": "Intro Data Mining & Anal"
353 | },
354 | {
355 | "number": "CS 4251",
356 | "name": "Computer Networking II"
357 | },
358 | {
359 | "number": "CS 4255",
360 | "name": "Intro-Network Management"
361 | },
362 | {
363 | "number": "CS 4260",
364 | "name": "Telecommunications Sys"
365 | },
366 | {
367 | "number": "CS 4261",
368 | "name": "Mobile Apps & Svcs"
369 | },
370 | {
371 | "number": "CS 4262",
372 | "name": "Network Security"
373 | },
374 | {
375 | "number": "CS 4263",
376 | "name": "Psychol of Cybersecurity"
377 | },
378 | {
379 | "number": "CS 4265",
380 | "name": "Intro to Blockchain"
381 | },
382 | {
383 | "number": "CS 4270",
384 | "name": "Data Communications Lab"
385 | },
386 | {
387 | "number": "CS 4280",
388 | "name": "Survey-Telecom & the Law"
389 | },
390 | {
391 | "number": "CS 4290",
392 | "name": "Advanced Computer Org"
393 | },
394 | {
395 | "number": "CS 4320",
396 | "name": "Software Processes"
397 | },
398 | {
399 | "number": "CS 4330",
400 | "name": "Software Applications"
401 | },
402 | {
403 | "number": "CS 4342",
404 | "name": "Software Generation"
405 | },
406 | {
407 | "number": "CS 4365",
408 | "name": "Intro Enterprise Comp"
409 | },
410 | {
411 | "number": "CS 4392",
412 | "name": "Programming Languages"
413 | },
414 | {
415 | "number": "CS 4400",
416 | "name": "Intr to Database Systems"
417 | },
418 | {
419 | "number": "CS 4420",
420 | "name": "Database Sys Implement"
421 | },
422 | {
423 | "number": "CS 4423",
424 | "name": "Adv Database Systems"
425 | },
426 | {
427 | "number": "CS 4432",
428 | "name": "Information Systems Dsgn"
429 | },
430 | {
431 | "number": "CS 4440",
432 | "name": "Database Technologies"
433 | },
434 | {
435 | "number": "CS 4452",
436 | "name": "Human-Centered Computing"
437 | },
438 | {
439 | "number": "CS 4455",
440 | "name": "Video Game Design"
441 | },
442 | {
443 | "number": "CS 4460",
444 | "name": "Intro Info Visualization"
445 | },
446 | {
447 | "number": "CS 4460R",
448 | "name": "CS 4460 Recitation"
449 | },
450 | {
451 | "number": "CS 4464",
452 | "name": "Computational Journalism"
453 | },
454 | {
455 | "number": "CS 4470",
456 | "name": "User Interface Software"
457 | },
458 | {
459 | "number": "CS 4472",
460 | "name": "Design of Online Comm"
461 | },
462 | {
463 | "number": "CS 4475",
464 | "name": "Comp Photography"
465 | },
466 | {
467 | "number": "CS 4476",
468 | "name": "Intro to Computer Vision"
469 | },
470 | {
471 | "number": "CS 4480",
472 | "name": "Digital Video Special FX"
473 | },
474 | {
475 | "number": "CS 4488",
476 | "name": "Procedural Content Gen"
477 | },
478 | {
479 | "number": "CS 4495",
480 | "name": "Computer Vision"
481 | },
482 | {
483 | "number": "CS 4496",
484 | "name": "Computer Animation"
485 | },
486 | {
487 | "number": "CS 4497",
488 | "name": "Comp Aesthetics"
489 | },
490 | {
491 | "number": "CS 4510",
492 | "name": "Automata and Complexity"
493 | },
494 | {
495 | "number": "CS 4520",
496 | "name": "Approximation Algs"
497 | },
498 | {
499 | "number": "CS 4530",
500 | "name": "Randomized Algs"
501 | },
502 | {
503 | "number": "CS 4540",
504 | "name": "Advanced Algs"
505 | },
506 | {
507 | "number": "CS 4550",
508 | "name": "Scientific Visualization"
509 | },
510 | {
511 | "number": "CS 4560",
512 | "name": "Verification of Systems"
513 | },
514 | {
515 | "number": "CS 4590",
516 | "name": "Computer Audio"
517 | },
518 | {
519 | "number": "CS 4605",
520 | "name": "Mobile&Ubiquitous Comp"
521 | },
522 | {
523 | "number": "CS 4611",
524 | "name": "AI Problem Solving"
525 | },
526 | {
527 | "number": "CS 4613",
528 | "name": "Knowledge Systems Engr"
529 | },
530 | {
531 | "number": "CS 4615",
532 | "name": "Knowledge-Based Modl&Dgn"
533 | },
534 | {
535 | "number": "CS 4616",
536 | "name": "Pattern Recognition"
537 | },
538 | {
539 | "number": "CS 4622",
540 | "name": "Case-Based Reasoning"
541 | },
542 | {
543 | "number": "CS 4625",
544 | "name": "Intel & Interactive Sys"
545 | },
546 | {
547 | "number": "CS 4632",
548 | "name": "Adv Intelligent Robotics"
549 | },
550 | {
551 | "number": "CS 4635",
552 | "name": "Knowledge-Based AI"
553 | },
554 | {
555 | "number": "CS 4641",
556 | "name": "Machine Learning"
557 | },
558 | {
559 | "number": "CS 4644",
560 | "name": "Deep Learning"
561 | },
562 | {
563 | "number": "CS 4646",
564 | "name": "Mach Learn for Trading"
565 | },
566 | {
567 | "number": "CS 4649",
568 | "name": "Robot Intelli Planning"
569 | },
570 | {
571 | "number": "CS 4650",
572 | "name": "Natural Language"
573 | },
574 | {
575 | "number": "CS 4660",
576 | "name": "Educational Technology"
577 | },
578 | {
579 | "number": "CS 4665",
580 | "name": "Educ Tech: Dsgn & Eval"
581 | },
582 | {
583 | "number": "CS 4670",
584 | "name": "CSCL"
585 | },
586 | {
587 | "number": "CS 4675",
588 | "name": "Internet Sys & Services"
589 | },
590 | {
591 | "number": "CS 4685",
592 | "name": "Pervasive Sys Networking"
593 | },
594 | {
595 | "number": "CS 4690",
596 | "name": "Empirical Methods in HCI"
597 | },
598 | {
599 | "number": "CS 4698",
600 | "name": "Research Assistantship"
601 | },
602 | {
603 | "number": "CS 4699",
604 | "name": "Undergraduate Research"
605 | },
606 | {
607 | "number": "CS 4710",
608 | "name": "CS for Bioinformatics"
609 | },
610 | {
611 | "number": "CS 4723",
612 | "name": "Inter Capstone Design"
613 | },
614 | {
615 | "number": "CS 4725",
616 | "name": "Info Security Policies"
617 | },
618 | {
619 | "number": "CS 4726",
620 | "name": "Privacy Tech Policy Law"
621 | },
622 | {
623 | "number": "CS 4731",
624 | "name": "Game AI"
625 | },
626 | {
627 | "number": "CS 4741",
628 | "name": "Int Mgt Dev - Proj Prep"
629 | },
630 | {
631 | "number": "CS 4742",
632 | "name": "Comp & Mgt Cap Proj"
633 | },
634 | {
635 | "number": "CS 4745",
636 | "name": "Info&Com Tech&Global Dev"
637 | },
638 | {
639 | "number": "CS 4752",
640 | "name": "Phil Issues-Computation"
641 | },
642 | {
643 | "number": "CS 4770",
644 | "name": "Mixed Reality Design"
645 | },
646 | {
647 | "number": "CS 4791",
648 | "name": "Integrative Proj-Cog Sci"
649 | },
650 | {
651 | "number": "CS 4792",
652 | "name": "Dsgn Proj-Cognitive Sci"
653 | },
654 | {
655 | "number": "CS 4793",
656 | "name": "Perspectives-Cog Science"
657 | },
658 | {
659 | "number": "CS 4795",
660 | "name": "GPU Prog for Video Games"
661 | },
662 | {
663 | "number": "CS 4801",
664 | "name": "Special Topics"
665 | },
666 | {
667 | "number": "CS 4802",
668 | "name": "Special Topics"
669 | },
670 | {
671 | "number": "CS 4803",
672 | "name": "Special Topics"
673 | },
674 | {
675 | "number": "CS 4804",
676 | "name": "Special Topics"
677 | },
678 | {
679 | "number": "CS 4805",
680 | "name": "Special Topics"
681 | },
682 | {
683 | "number": "CS 4816",
684 | "name": "Special Topics"
685 | },
686 | {
687 | "number": "CS 4851",
688 | "name": "Special Topics"
689 | },
690 | {
691 | "number": "CS 4853",
692 | "name": "Special Topics"
693 | },
694 | {
695 | "number": "CS 4854",
696 | "name": "Special Topics"
697 | },
698 | {
699 | "number": "CS 4863",
700 | "name": "Special Topics"
701 | },
702 | {
703 | "number": "CS 4873",
704 | "name": "Special Topics"
705 | },
706 | {
707 | "number": "CS 4883",
708 | "name": "Special Topics"
709 | },
710 | {
711 | "number": "CS 4893",
712 | "name": "Special Topics"
713 | },
714 | {
715 | "number": "CS 4901",
716 | "name": "Spec Prob-Computer Sci"
717 | },
718 | {
719 | "number": "CS 4902",
720 | "name": "Spec Prob-Computer Sci"
721 | },
722 | {
723 | "number": "CS 4903",
724 | "name": "Spec Prob-Computer Sci"
725 | },
726 | {
727 | "number": "CS 4911",
728 | "name": "Design Capstone Project"
729 | },
730 | {
731 | "number": "CS 4912",
732 | "name": "Design Capstone Project"
733 | },
734 | {
735 | "number": "CS 4980",
736 | "name": "Research Capstone Proj"
737 | },
738 | {
739 | "number": "CS 4XXX",
740 | "name": "Computer Sci Elective"
741 | },
742 | {
743 | "number": "CS 6010",
744 | "name": "Principles of Design"
745 | },
746 | {
747 | "number": "CS 6035",
748 | "name": "Intro To Info Security"
749 | },
750 | {
751 | "number": "CS 6150",
752 | "name": "Computing For Good"
753 | },
754 | {
755 | "number": "CS 6200",
756 | "name": "Graduate Intro to OS"
757 | },
758 | {
759 | "number": "CS 6210",
760 | "name": "Adv Operating Systems"
761 | },
762 | {
763 | "number": "CS 6211",
764 | "name": "Sys Design Cloud Comput"
765 | },
766 | {
767 | "number": "CS 6220",
768 | "name": "Big Data Sys & Analytics"
769 | },
770 | {
771 | "number": "CS 6230",
772 | "name": "High Perf Parallel Comp"
773 | },
774 | {
775 | "number": "CS 6235",
776 | "name": "Real-Time Systems"
777 | },
778 | {
779 | "number": "CS 6238",
780 | "name": "Secure Computer Systems"
781 | },
782 | {
783 | "number": "CS 6241",
784 | "name": "Compiler Design"
785 | },
786 | {
787 | "number": "CS 6245",
788 | "name": "Parallelizing Compilers"
789 | },
790 | {
791 | "number": "CS 6246",
792 | "name": "Object-Oriented Systems"
793 | },
794 | {
795 | "number": "CS 6250",
796 | "name": "Computer Networks"
797 | },
798 | {
799 | "number": "CS 6255",
800 | "name": "Network Management"
801 | },
802 | {
803 | "number": "CS 6260",
804 | "name": "Applied Cryptography"
805 | },
806 | {
807 | "number": "CS 6261",
808 | "name": "Cyber Incident Response"
809 | },
810 | {
811 | "number": "CS 6262",
812 | "name": "Network Security"
813 | },
814 | {
815 | "number": "CS 6263",
816 | "name": "Intro Cyber Phys Sys Sec"
817 | },
818 | {
819 | "number": "CS 6264",
820 | "name": "Infosec Labs: Defenses"
821 | },
822 | {
823 | "number": "CS 6265",
824 | "name": "Info Sec Lab Binexp"
825 | },
826 | {
827 | "number": "CS 6266",
828 | "name": "Info Security Practicum"
829 | },
830 | {
831 | "number": "CS 6267",
832 | "name": "Critical Infrastructures"
833 | },
834 | {
835 | "number": "CS 6268",
836 | "name": "Psychol of Cybersecurity"
837 | },
838 | {
839 | "number": "CS 6269",
840 | "name": "Formal Info Assur Model"
841 | },
842 | {
843 | "number": "CS 6280",
844 | "name": "Eval Communication Nets"
845 | },
846 | {
847 | "number": "CS 6290",
848 | "name": "High Perform Comput Arch"
849 | },
850 | {
851 | "number": "CS 6291",
852 | "name": "Embedded Software Opt."
853 | },
854 | {
855 | "number": "CS 6300",
856 | "name": "Software Dev Process"
857 | },
858 | {
859 | "number": "CS 6301",
860 | "name": "Adv Software Engineering"
861 | },
862 | {
863 | "number": "CS 6310",
864 | "name": "Software Arch & Design"
865 | },
866 | {
867 | "number": "CS 6320",
868 | "name": "Requirements Analysis"
869 | },
870 | {
871 | "number": "CS 6330",
872 | "name": "Software Generation&Test"
873 | },
874 | {
875 | "number": "CS 6340",
876 | "name": "Software Analysis & Test"
877 | },
878 | {
879 | "number": "CS 6365",
880 | "name": "Intro Enterprise Comput."
881 | },
882 | {
883 | "number": "CS 6390",
884 | "name": "Programming Languages"
885 | },
886 | {
887 | "number": "CS 6400",
888 | "name": "DB Sys Concepts& Design"
889 | },
890 | {
891 | "number": "CS 6402",
892 | "name": "Databases and Infosec"
893 | },
894 | {
895 | "number": "CS 6411",
896 | "name": "O-O Database Model & Sys"
897 | },
898 | {
899 | "number": "CS 6421",
900 | "name": "Tempor,Spatial&Active DB"
901 | },
902 | {
903 | "number": "CS 6422",
904 | "name": "Database System Implemnt"
905 | },
906 | {
907 | "number": "CS 6423",
908 | "name": "Adv Database Systems"
909 | },
910 | {
911 | "number": "CS 6430",
912 | "name": "Parallel&Distributed DB"
913 | },
914 | {
915 | "number": "CS 6440",
916 | "name": "Intro Health Informatics"
917 | },
918 | {
919 | "number": "CS 6451",
920 | "name": "Human-Centered Computing"
921 | },
922 | {
923 | "number": "CS 6452",
924 | "name": "Prototyping Interact Sys"
925 | },
926 | {
927 | "number": "CS 6454",
928 | "name": "Qualitative Methods HCI"
929 | },
930 | {
931 | "number": "CS 6455",
932 | "name": "User Interface Dsgn&Eval"
933 | },
934 | {
935 | "number": "CS 6456",
936 | "name": "Principles-UI Software"
937 | },
938 | {
939 | "number": "CS 6457",
940 | "name": "Video Game Design"
941 | },
942 | {
943 | "number": "CS 6460",
944 | "name": "Educ Tech-Foundations"
945 | },
946 | {
947 | "number": "CS 6461",
948 | "name": "Computing Ed Research"
949 | },
950 | {
951 | "number": "CS 6465",
952 | "name": "Computational Journalism"
953 | },
954 | {
955 | "number": "CS 6470",
956 | "name": "Online Communities"
957 | },
958 | {
959 | "number": "CS 6471",
960 | "name": "Comp Social Science"
961 | },
962 | {
963 | "number": "CS 6474",
964 | "name": "Social Computing"
965 | },
966 | {
967 | "number": "CS 6475",
968 | "name": "Comp. Photography"
969 | },
970 | {
971 | "number": "CS 6476",
972 | "name": "Computer Vision"
973 | },
974 | {
975 | "number": "CS 6480",
976 | "name": "Comp Visualiz Techniques"
977 | },
978 | {
979 | "number": "CS 6485",
980 | "name": "Visual Meth-Sci & Engr"
981 | },
982 | {
983 | "number": "CS 6491",
984 | "name": "Computer Graphics"
985 | },
986 | {
987 | "number": "CS 6492",
988 | "name": "Shape Grammars"
989 | },
990 | {
991 | "number": "CS 6497",
992 | "name": "Comp Aesthetics"
993 | },
994 | {
995 | "number": "CS 6505",
996 | "name": "Computability&Algorithms"
997 | },
998 | {
999 | "number": "CS 6515",
1000 | "name": "Intro to Grad Algorithms"
1001 | },
1002 | {
1003 | "number": "CS 6520",
1004 | "name": "Computational Complexity"
1005 | },
1006 | {
1007 | "number": "CS 6550",
1008 | "name": "Design& Analy-Algorithms"
1009 | },
1010 | {
1011 | "number": "CS 6601",
1012 | "name": "Artificial Intelligence"
1013 | },
1014 | {
1015 | "number": "CS 6603",
1016 | "name": "AI Ethics Society"
1017 | },
1018 | {
1019 | "number": "CS 6641",
1020 | "name": "Personal Health Inform"
1021 | },
1022 | {
1023 | "number": "CS 6670",
1024 | "name": "Distrib Cntrl Algorithms"
1025 | },
1026 | {
1027 | "number": "CS 6675",
1028 | "name": "Advance Internet Comput"
1029 | },
1030 | {
1031 | "number": "CS 6705",
1032 | "name": "Applications of AI"
1033 | },
1034 | {
1035 | "number": "CS 6725",
1036 | "name": "Info Security Policies"
1037 | },
1038 | {
1039 | "number": "CS 6726",
1040 | "name": "Privacy Tech Policy Law"
1041 | },
1042 | {
1043 | "number": "CS 6727",
1044 | "name": "Cyber Sec Practicum"
1045 | },
1046 | {
1047 | "number": "CS 6730",
1048 | "name": "Data Vis Principles"
1049 | },
1050 | {
1051 | "number": "CS 6745",
1052 | "name": "Info&Com Tech&Global Dev"
1053 | },
1054 | {
1055 | "number": "CS 6747",
1056 | "name": "ADV Malware Analysis"
1057 | },
1058 | {
1059 | "number": "CS 6750",
1060 | "name": "Human-Computer Interact"
1061 | },
1062 | {
1063 | "number": "CS 6753",
1064 | "name": "HCI Prof Prep & Practice"
1065 | },
1066 | {
1067 | "number": "CS 6754",
1068 | "name": "Engr Database Mgt System"
1069 | },
1070 | {
1071 | "number": "CS 6755",
1072 | "name": "HCI Foundations"
1073 | },
1074 | {
1075 | "number": "CS 6763",
1076 | "name": "Design of Environments"
1077 | },
1078 | {
1079 | "number": "CS 6764",
1080 | "name": "Geometric Modeling"
1081 | },
1082 | {
1083 | "number": "CS 6770",
1084 | "name": "Mixed Reality Design"
1085 | },
1086 | {
1087 | "number": "CS 6780",
1088 | "name": "Medical Image Processing"
1089 | },
1090 | {
1091 | "number": "CS 6795",
1092 | "name": "Intro-Cognitive Science"
1093 | },
1094 | {
1095 | "number": "CS 6998",
1096 | "name": "HCI Master's Project"
1097 | },
1098 | {
1099 | "number": "CS 6999",
1100 | "name": "Master's Project"
1101 | },
1102 | {
1103 | "number": "CS 6XXX",
1104 | "name": "Computer Sci Elective"
1105 | },
1106 | {
1107 | "number": "CS 7000",
1108 | "name": "Master's Thesis"
1109 | },
1110 | {
1111 | "number": "CS 7001",
1112 | "name": "Grad Studies-Computing"
1113 | },
1114 | {
1115 | "number": "CS 7110",
1116 | "name": "Parallel Computer Arch"
1117 | },
1118 | {
1119 | "number": "CS 7210",
1120 | "name": "Distributed Computing"
1121 | },
1122 | {
1123 | "number": "CS 7230",
1124 | "name": "Software Dsgn,Impl& Eval"
1125 | },
1126 | {
1127 | "number": "CS 7250",
1128 | "name": "Broadband Networking Sys"
1129 | },
1130 | {
1131 | "number": "CS 7260",
1132 | "name": "Internet Arch& Protocols"
1133 | },
1134 | {
1135 | "number": "CS 7270",
1136 | "name": "Networked Apps&Services"
1137 | },
1138 | {
1139 | "number": "CS 7280",
1140 | "name": "Network Science"
1141 | },
1142 | {
1143 | "number": "CS 7290",
1144 | "name": "Adv. Microarchitecture"
1145 | },
1146 | {
1147 | "number": "CS 7292",
1148 | "name": "Reliable Secure Comparch"
1149 | },
1150 | {
1151 | "number": "CS 7400",
1152 | "name": "Intro Quantum Computing"
1153 | },
1154 | {
1155 | "number": "CS 7450",
1156 | "name": "Inform Visualization"
1157 | },
1158 | {
1159 | "number": "CS 7451",
1160 | "name": "Hum- Center Data Analysis"
1161 | },
1162 | {
1163 | "number": "CS 7455",
1164 | "name": "Issues/Human-Center Comp"
1165 | },
1166 | {
1167 | "number": "CS 7460",
1168 | "name": "Collaborative Computing"
1169 | },
1170 | {
1171 | "number": "CS 7465",
1172 | "name": "Edu Tech-Design and Eval"
1173 | },
1174 | {
1175 | "number": "CS 7467",
1176 | "name": "Comp Collaborative Learn"
1177 | },
1178 | {
1179 | "number": "CS 7470",
1180 | "name": "Ubiquitous Computing"
1181 | },
1182 | {
1183 | "number": "CS 7476",
1184 | "name": "Advanced Computer Vision"
1185 | },
1186 | {
1187 | "number": "CS 7490",
1188 | "name": "Adv Image Synthesis"
1189 | },
1190 | {
1191 | "number": "CS 7491",
1192 | "name": "3D Complexity"
1193 | },
1194 | {
1195 | "number": "CS 7492",
1196 | "name": "Simulation of Biology"
1197 | },
1198 | {
1199 | "number": "CS 7495",
1200 | "name": "Computer Vision"
1201 | },
1202 | {
1203 | "number": "CS 7496",
1204 | "name": "Computer Animation"
1205 | },
1206 | {
1207 | "number": "CS 7497",
1208 | "name": "Virtual Environments"
1209 | },
1210 | {
1211 | "number": "CS 7499",
1212 | "name": "3D Reconstruction"
1213 | },
1214 | {
1215 | "number": "CS 7510",
1216 | "name": "Graph Algorithm"
1217 | },
1218 | {
1219 | "number": "CS 7520",
1220 | "name": "Approximation Algorithms"
1221 | },
1222 | {
1223 | "number": "CS 7525",
1224 | "name": "Algorithmic Game Theory"
1225 | },
1226 | {
1227 | "number": "CS 7530",
1228 | "name": "Randomized Algorithms"
1229 | },
1230 | {
1231 | "number": "CS 7535",
1232 | "name": "Markov Chain Monte Carlo"
1233 | },
1234 | {
1235 | "number": "CS 7540",
1236 | "name": "Spectral Algorithms"
1237 | },
1238 | {
1239 | "number": "CS 7545",
1240 | "name": "Machine Learning Theory"
1241 | },
1242 | {
1243 | "number": "CS 7560",
1244 | "name": "Theory of Cryptography"
1245 | },
1246 | {
1247 | "number": "CS 7610",
1248 | "name": "Modeling and Design"
1249 | },
1250 | {
1251 | "number": "CS 7611",
1252 | "name": "AI Problem Solving"
1253 | },
1254 | {
1255 | "number": "CS 7612",
1256 | "name": "AI Planning"
1257 | },
1258 | {
1259 | "number": "CS 7613",
1260 | "name": "Knowledge Systems Engr"
1261 | },
1262 | {
1263 | "number": "CS 7615",
1264 | "name": "Knowledge Agents"
1265 | },
1266 | {
1267 | "number": "CS 7616",
1268 | "name": "Pattern Recognition"
1269 | },
1270 | {
1271 | "number": "CS 7620",
1272 | "name": "Case-Based Reasoning"
1273 | },
1274 | {
1275 | "number": "CS 7626",
1276 | "name": "Behavioral Imaging"
1277 | },
1278 | {
1279 | "number": "CS 7630",
1280 | "name": "Autonomous Robotics"
1281 | },
1282 | {
1283 | "number": "CS 7631",
1284 | "name": "Multi-Robot Systems"
1285 | },
1286 | {
1287 | "number": "CS 7632",
1288 | "name": "Game AI"
1289 | },
1290 | {
1291 | "number": "CS 7633",
1292 | "name": "Human-Robot Interaction"
1293 | },
1294 | {
1295 | "number": "CS 7634",
1296 | "name": "AI Storytell In Vir Wrld"
1297 | },
1298 | {
1299 | "number": "CS 7636",
1300 | "name": "Computational Perception"
1301 | },
1302 | {
1303 | "number": "CS 7637",
1304 | "name": "Knowledge-Based AI"
1305 | },
1306 | {
1307 | "number": "CS 7638",
1308 | "name": "Robotics: AI Techniques"
1309 | },
1310 | {
1311 | "number": "CS 7639",
1312 | "name": "Cyber Physical Design"
1313 | },
1314 | {
1315 | "number": "CS 7640",
1316 | "name": "Learning in Auton Agents"
1317 | },
1318 | {
1319 | "number": "CS 7641",
1320 | "name": "Machine Learning"
1321 | },
1322 | {
1323 | "number": "CS 7642",
1324 | "name": "Reinforcement Learning"
1325 | },
1326 | {
1327 | "number": "CS 7643",
1328 | "name": "Deep Learning"
1329 | },
1330 | {
1331 | "number": "CS 7644",
1332 | "name": "ML For Robotics"
1333 | },
1334 | {
1335 | "number": "CS 7645",
1336 | "name": "Num Machine Learning"
1337 | },
1338 | {
1339 | "number": "CS 7646",
1340 | "name": "Mach Learn For Trading"
1341 | },
1342 | {
1343 | "number": "CS 7647",
1344 | "name": "Mach Lrn w/ltd Supervis"
1345 | },
1346 | {
1347 | "number": "CS 7648",
1348 | "name": "Interactive Robo Learn"
1349 | },
1350 | {
1351 | "number": "CS 7649",
1352 | "name": "Robot Intelli: Planning"
1353 | },
1354 | {
1355 | "number": "CS 7650",
1356 | "name": "Natural Language"
1357 | },
1358 | {
1359 | "number": "CS 7651",
1360 | "name": "Human & Machine Learning"
1361 | },
1362 | {
1363 | "number": "CS 7695",
1364 | "name": "Phil of Cognition"
1365 | },
1366 | {
1367 | "number": "CS 7697",
1368 | "name": "Cognitive Model Sci&Tech"
1369 | },
1370 | {
1371 | "number": "CS 7741",
1372 | "name": "Robotics Pro Prep 1"
1373 | },
1374 | {
1375 | "number": "CS 7742",
1376 | "name": "Robo Pro Prep 2"
1377 | },
1378 | {
1379 | "number": "CS 7743",
1380 | "name": "Robo Pro Prep 3"
1381 | },
1382 | {
1383 | "number": "CS 7750",
1384 | "name": "Math Fnd Of Machine Lrn"
1385 | },
1386 | {
1387 | "number": "CS 7751",
1388 | "name": "Graphical Models in ML"
1389 | },
1390 | {
1391 | "number": "CS 7785",
1392 | "name": "Intro Robotics Research"
1393 | },
1394 | {
1395 | "number": "CS 7790",
1396 | "name": "Cognitive Modeling"
1397 | },
1398 | {
1399 | "number": "CS 7999",
1400 | "name": "Prep-Doctoral Qual Exams"
1401 | },
1402 | {
1403 | "number": "CS 8001",
1404 | "name": "Seminar"
1405 | },
1406 | {
1407 | "number": "CS 8002",
1408 | "name": "Seminar"
1409 | },
1410 | {
1411 | "number": "CS 8003",
1412 | "name": "Seminar"
1413 | },
1414 | {
1415 | "number": "CS 8004",
1416 | "name": "Seminar"
1417 | },
1418 | {
1419 | "number": "CS 8005",
1420 | "name": "Seminar"
1421 | },
1422 | {
1423 | "number": "CS 8006",
1424 | "name": "Seminar"
1425 | },
1426 | {
1427 | "number": "CS 8030",
1428 | "name": "Software Engr Seminar"
1429 | },
1430 | {
1431 | "number": "CS 8740",
1432 | "name": "Robotics Internship"
1433 | },
1434 | {
1435 | "number": "CS 8741",
1436 | "name": "Robo Capstone Project"
1437 | },
1438 | {
1439 | "number": "CS 8750",
1440 | "name": "Robotics Research Fnd I"
1441 | },
1442 | {
1443 | "number": "CS 8751",
1444 | "name": "Robotics Research Fnd II"
1445 | },
1446 | {
1447 | "number": "CS 8795",
1448 | "name": "Colloquium-Cognitive Sci"
1449 | },
1450 | {
1451 | "number": "CS 8801",
1452 | "name": "Special Topics"
1453 | },
1454 | {
1455 | "number": "CS 8802",
1456 | "name": "Special Topics"
1457 | },
1458 | {
1459 | "number": "CS 8803",
1460 | "name": "Special Topics"
1461 | },
1462 | {
1463 | "number": "CS 8804",
1464 | "name": "Special Topics"
1465 | },
1466 | {
1467 | "number": "CS 8805",
1468 | "name": "Special Topics"
1469 | },
1470 | {
1471 | "number": "CS 8806",
1472 | "name": "Special Topics"
1473 | },
1474 | {
1475 | "number": "CS 8811",
1476 | "name": "Special Topics"
1477 | },
1478 | {
1479 | "number": "CS 8813",
1480 | "name": "Special Topics"
1481 | },
1482 | {
1483 | "number": "CS 8816",
1484 | "name": "Special Topics"
1485 | },
1486 | {
1487 | "number": "CS 8873",
1488 | "name": "Special Topics"
1489 | },
1490 | {
1491 | "number": "CS 8893",
1492 | "name": "Spec Top-Cognitive Sci"
1493 | },
1494 | {
1495 | "number": "CS 8901",
1496 | "name": "Special Problems"
1497 | },
1498 | {
1499 | "number": "CS 8902",
1500 | "name": "Special Problems"
1501 | },
1502 | {
1503 | "number": "CS 8903",
1504 | "name": "Special Problems"
1505 | },
1506 | {
1507 | "number": "CS 8997",
1508 | "name": "Teaching Assistantship"
1509 | },
1510 | {
1511 | "number": "CS 8998",
1512 | "name": "Research Assistantship"
1513 | },
1514 | {
1515 | "number": "CS 8999",
1516 | "name": "Doctoral Thesis Prep"
1517 | },
1518 | {
1519 | "number": "CS 9000",
1520 | "name": "Doctoral Thesis"
1521 | }
1522 | ]
--------------------------------------------------------------------------------
/backend/api/utils/JSON_inputs/faculty_list.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "Jacob Abernethy"
4 | },
5 | {
6 | "name": "Mustaque Ahamad"
7 | },
8 | {
9 | "name": "Mostafa Ammar"
10 | },
11 | {
12 | "name": "Joy Arulraj"
13 | },
14 | {
15 | "name": "Ramin Ayanzadeh"
16 | },
17 | {
18 | "name": "Suguman Bansal"
19 | },
20 | {
21 | "name": "Ketan Bhardwaj"
22 | },
23 | {
24 | "name": "Douglas Blough"
25 | },
26 | {
27 | "name": "Gregory Bodwin"
28 | },
29 | {
30 | "name": "Alexandra Boldyreva"
31 | },
32 | {
33 | "name": "Xu Chu"
34 | },
35 | {
36 | "name": "Pak Ho (Simon) Chung"
37 | },
38 | {
39 | "name": "Russell Clark"
40 | },
41 | {
42 | "name": "Thomas Conte"
43 | },
44 | {
45 | "name": "Tom Conte"
46 | },
47 | {
48 | "name": "Rachel Cummings"
49 | },
50 | {
51 | "name": "Alexandros Daglis"
52 | },
53 | {
54 | "name": "Alberto Dainotti"
55 | },
56 | {
57 | "name": "Richard DeMillo"
58 | },
59 | {
60 | "name": "David Devecsery"
61 | },
62 | {
63 | "name": "Ashutosh Dhekne"
64 | },
65 | {
66 | "name": "Constantine Dovrolis"
67 | },
68 | {
69 | "name": "Alex Duncan"
70 | },
71 | {
72 | "name": "Greg Eisenhauer"
73 | },
74 | {
75 | "name": "Merrick Furst"
76 | },
77 | {
78 | "name": "Zvi Galil"
79 | },
80 | {
81 | "name": "Vijay Ganesh"
82 | },
83 | {
84 | "name": "Ada Gavrilovska"
85 | },
86 | {
87 | "name": "Daniel Genkin"
88 | },
89 | {
90 | "name": "Max Grossman"
91 | },
92 | {
93 | "name": "Swati Gupta"
94 | },
95 | {
96 | "name": "Akihiro Hayashi"
97 | },
98 | {
99 | "name": "Anand Padmanabha Iyer"
100 | },
101 | {
102 | "name": "Anand Padmanabha Iyer"
103 | },
104 | {
105 | "name": "ChulWon Kang"
106 | },
107 | {
108 | "name": "Hyesoon Kim"
109 | },
110 | {
111 | "name": "Taesoo Kim"
112 | },
113 | {
114 | "name": "Scott Klasky"
115 | },
116 | {
117 | "name": "Vladimir Kolesnikov"
118 | },
119 | {
120 | "name": "Maria Konte"
121 | },
122 | {
123 | "name": "Kevin Koo"
124 | },
125 | {
126 | "name": "Tushar Krishna"
127 | },
128 | {
129 | "name": "Wenke Lee"
130 | },
131 | {
132 | "name": "Yingyan (Celine) Lin"
133 | },
134 | {
135 | "name": "Ling Liu"
136 | },
137 | {
138 | "name": "Divya Mahajan"
139 | },
140 | {
141 | "name": "Yisreol Mirsky"
142 | },
143 | {
144 | "name": "Vincent Mooney"
145 | },
146 | {
147 | "name": "Shamkant Navathe"
148 | },
149 | {
150 | "name": "Alessandro Orso"
151 | },
152 | {
153 | "name": "Santosh Pande"
154 | },
155 | {
156 | "name": "Sunjae Park"
157 | },
158 | {
159 | "name": "Sunjae Park"
160 | },
161 | {
162 | "name": "Sri Raj Paul"
163 | },
164 | {
165 | "name": "Paul Pearce"
166 | },
167 | {
168 | "name": "Richard Peng"
169 | },
170 | {
171 | "name": "Roberto Perdisci"
172 | },
173 | {
174 | "name": "Will Perkins"
175 | },
176 | {
177 | "name": "Milos Prvulovic"
178 | },
179 | {
180 | "name": "Calton Pu"
181 | },
182 | {
183 | "name": "Moinuddin Qureshi"
184 | },
185 | {
186 | "name": "Umakishore Ramachandran"
187 | },
188 | {
189 | "name": "Dana Randall"
190 | },
191 | {
192 | "name": "Kexin Rong"
193 | },
194 | {
195 | "name": "Ahmed Saeed"
196 | },
197 | {
198 | "name": "Vivek Sarkar"
199 | },
200 | {
201 | "name": "Jun Shirako"
202 | },
203 | {
204 | "name": "Sahil Singla"
205 | },
206 | {
207 | "name": "Michael Specter"
208 | },
209 | {
210 | "name": "Cecilia Testart"
211 | },
212 | {
213 | "name": "Prasad Tetali"
214 | },
215 | {
216 | "name": "Alexey Tumanov"
217 | },
218 | {
219 | "name": "Jan van den Brand"
220 | },
221 | {
222 | "name": "Santosh Vempala"
223 | },
224 | {
225 | "name": "Qi Xin"
226 | },
227 | {
228 | "name": "Jun Xu"
229 | },
230 | {
231 | "name": "Guangliang Yang"
232 | },
233 | {
234 | "name": "Jeffrey Young"
235 | },
236 | {
237 | "name": "Ellen Zegura"
238 | },
239 | {
240 | "name": "Qirun Zhang"
241 | },
242 | {
243 | "name": "Jisheng Zhao"
244 | }
245 | ]
--------------------------------------------------------------------------------
/backend/api/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmanTahiliani/PeerNotes/46a4574f62d46a0110f9324f7d0e23b3c8f3f9bc/backend/api/utils/__init__.py
--------------------------------------------------------------------------------
/backend/api/utils/get_client_ip.py:
--------------------------------------------------------------------------------
1 | from typing import Any
2 | from django.utils import timezone
3 |
4 |
5 | def get_client_ip(request):
6 | x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
7 | if x_forwarded_for:
8 | ip = x_forwarded_for.split(",")[0]
9 | else:
10 | ip = request.META.get("REMOTE_ADDR")
11 | return ip
12 |
13 |
14 | def update_user_ip(request):
15 | user = request.user
16 | try:
17 | data = request.data
18 | ip_address = data["ip"]
19 | print("Local IP found in request")
20 | except Exception as e:
21 | ip_address = get_client_ip(request)
22 | print("Using public IP")
23 | user.ip_address = ip_address
24 | user.last_poll = timezone.now()
25 | user.save()
26 |
27 |
28 | class PollMiddleware:
29 | def __init__(self, get_response):
30 | self.get_response = get_response
31 |
32 | def __call__(self, request):
33 | print("Updating user last online and IP address")
34 | update_user_ip(request)
35 | response = self.get_response(request)
36 |
37 | return response
38 |
--------------------------------------------------------------------------------
/backend/api/views.py:
--------------------------------------------------------------------------------
1 | from api.models import Course, File, Professor, Semester, Topic, PeerUser, UserReport
2 | from api.serializers import (
3 | CourseSerializer,
4 | FileSerializer,
5 | ProfessorSerializer,
6 | SemesterSerializer,
7 | TopicSerializer,
8 | UserSerializer,
9 | )
10 | from datetime import timedelta
11 | from django.contrib.auth import authenticate
12 | from django.db import transaction
13 | from django.db.models import Count, Value, F, IntegerField
14 | from django.utils import timezone
15 | from rest_framework import generics, status
16 | from rest_framework.authentication import TokenAuthentication
17 | from rest_framework.permissions import IsAuthenticated
18 | from rest_framework.response import Response
19 | from rest_framework.views import APIView
20 | from rest_framework import filters
21 | from rest_framework.exceptions import ValidationError, NotFound
22 | from api.utils.get_client_ip import update_user_ip
23 |
24 |
25 | class TopicListCreateAPIView(generics.ListCreateAPIView):
26 | queryset = Topic.objects.all()
27 | serializer_class = TopicSerializer
28 | authentication_classes = [TokenAuthentication]
29 | permission_classes = [IsAuthenticated]
30 | filter_backends = [filters.SearchFilter]
31 | search_fields = ["name"]
32 |
33 |
34 | class TopicDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
35 | queryset = Topic.objects.all()
36 | serializer_class = TopicSerializer
37 | authentication_classes = [TokenAuthentication]
38 | permission_classes = [IsAuthenticated]
39 |
40 |
41 | class ProfessorListCreateAPIView(generics.ListCreateAPIView):
42 | queryset = Professor.objects.all()
43 | serializer_class = ProfessorSerializer
44 | authentication_classes = [TokenAuthentication]
45 | permission_classes = [IsAuthenticated]
46 | filter_backends = [filters.SearchFilter]
47 | search_fields = ["name"]
48 |
49 |
50 | class ProfessorDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
51 | queryset = Professor.objects.all()
52 | serializer_class = ProfessorSerializer
53 | authentication_classes = [TokenAuthentication]
54 | permission_classes = [IsAuthenticated]
55 |
56 |
57 | class SemesterListCreateAPIView(generics.ListCreateAPIView):
58 | queryset = Semester.objects.all()
59 | serializer_class = SemesterSerializer
60 | authentication_classes = [TokenAuthentication]
61 | permission_classes = [IsAuthenticated]
62 | filter_backends = [filters.SearchFilter]
63 | search_fields = ["name"]
64 |
65 |
66 | class SemesterDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
67 | queryset = Semester.objects.all()
68 | serializer_class = SemesterSerializer
69 | authentication_classes = [TokenAuthentication]
70 | permission_classes = [IsAuthenticated]
71 |
72 | def delete(self, request, *args, **kwargs):
73 | instance = self.get_object()
74 | self.perform_destroy(instance)
75 | return Response(status=status.HTTP_204_NO_CONTENT)
76 |
77 |
78 | class CourseListCreateAPIView(generics.ListCreateAPIView):
79 | queryset = Course.objects.all()
80 | serializer_class = CourseSerializer
81 | authentication_classes = [TokenAuthentication]
82 | permission_classes = [IsAuthenticated]
83 | filter_backends = [filters.SearchFilter]
84 | search_fields = ["name"]
85 |
86 |
87 | class CourseDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
88 | queryset = Course.objects.all()
89 | serializer_class = CourseSerializer
90 | authentication_classes = [TokenAuthentication]
91 | permission_classes = [IsAuthenticated]
92 |
93 |
94 | class FileListCreateAPIView(generics.ListCreateAPIView):
95 | queryset = File.objects.all()
96 | serializer_class = FileSerializer
97 | authentication_classes = [TokenAuthentication]
98 | permission_classes = [IsAuthenticated]
99 | filter_backends = [filters.SearchFilter]
100 | search_fields = ["name"]
101 |
102 |
103 | class FileDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
104 | queryset = File.objects.all()
105 | serializer_class = FileSerializer
106 | authentication_classes = [TokenAuthentication]
107 | permission_classes = [IsAuthenticated]
108 |
109 |
110 | class UpvoteFile(APIView):
111 | authentication_classes = [TokenAuthentication]
112 | permission_classes = [IsAuthenticated]
113 |
114 | def post(self, request, file_id):
115 | user = request.user
116 | # update_user_ip(request)
117 |
118 | try:
119 | file = File.objects.get(id=file_id)
120 | if file.downvotes.contains(user):
121 | file.downvotes.remove(user)
122 | file.upvotes.add(user)
123 | file.save()
124 | return Response(
125 | {"msg": f"Upvoted {file.filename}", "file_points": file.points},
126 | status=status.HTTP_200_OK,
127 | )
128 | except Exception as e:
129 | print(f"Error: {str(e)}")
130 | return Response(
131 | {"error": "Please enter valid file id"},
132 | status=status.HTTP_400_BAD_REQUEST,
133 | )
134 |
135 |
136 | class DownvoteFile(APIView):
137 | authentication_classes = [TokenAuthentication]
138 | permission_classes = [IsAuthenticated]
139 |
140 | def post(self, request, file_id):
141 | user = request.user
142 | # update_user_ip(request)
143 |
144 | try:
145 | file = File.objects.get(id=file_id)
146 | if file.upvotes.contains(user):
147 | file.upvotes.remove(user)
148 | file.downvotes.add(user)
149 | file.save()
150 | return Response(
151 | {"msg": f"Downvoted {file.filename}", "file_points": file.points},
152 | status=status.HTTP_200_OK,
153 | )
154 | except Exception as e:
155 | print(f"Error: {str(e)}")
156 | return Response(
157 | {"error": "Please enter valid file id"},
158 | status=status.HTTP_400_BAD_REQUEST,
159 | )
160 |
161 |
162 | class RegisterFile(APIView):
163 | authentication_classes = [TokenAuthentication]
164 | permission_classes = [IsAuthenticated]
165 |
166 | def post(self, request):
167 | user = request.user
168 | # update_user_ip(request)
169 | data = request.data
170 | try:
171 | with transaction.atomic():
172 | user.last_poll = timezone.now()
173 | required_fields = set(
174 | ["filename", "topic", "semester", "professor", "course"]
175 | )
176 | provided_fields = set(request.data.keys())
177 | missing_fields = required_fields - provided_fields
178 |
179 | if any(["filename", "topic"]) in missing_fields:
180 | raise ValidationError(
181 | "Missing one or more of the required field(s): filename, topic"
182 | )
183 |
184 | file = File.objects.create(
185 | filename=data["filename"], original_author=user
186 | )
187 | file.peer_users.add(user)
188 |
189 | file.course = (
190 | Course.objects.get(id=data.get("course"))
191 | if data.get("course")
192 | else None
193 | )
194 | file.professor = (
195 | Professor.objects.get(id=data.get("professor"))
196 | if data.get("professor")
197 | else None
198 | )
199 | file.semester = (
200 | Semester.objects.get(id=data.get("semester"))
201 | if data.get("semester")
202 | else None
203 | )
204 |
205 | topic_id = data["topic"]
206 |
207 | try:
208 | topic = Topic.objects.get(id=topic_id)
209 | file.topic = topic
210 | except Exception as e:
211 | topic_serializer = TopicSerializer(data=topic_id)
212 | if topic_serializer.is_valid():
213 | topic = topic_serializer.save()
214 | file.topic = topic
215 |
216 | response_data = {
217 | "id": file.id,
218 | "filename": file.filename,
219 | "topic": file.topic.name if file.topic else None,
220 | "semester": file.semester.name if file.semester else None,
221 | "course": file.course.name if file.course else None,
222 | "professor": file.professor.name if file.professor else None,
223 | }
224 | user.points += 1
225 | file.save()
226 | user.save()
227 | return Response(
228 | response_data,
229 | status=status.HTTP_201_CREATED,
230 | )
231 | except Exception as e:
232 | print("Error", str(e))
233 | return Response(
234 | {"error(s)": "Something went wrong"}, status=status.HTTP_400_BAD_REQUEST
235 | )
236 |
237 |
238 | class AddPeerToFile(APIView):
239 | authentication_classes = [TokenAuthentication]
240 | permission_classes = [IsAuthenticated]
241 |
242 | def post(self, request, file_id):
243 | try:
244 | user = request.user
245 | file = File.objects.get(id=file_id)
246 | print("File fetched")
247 | print(file)
248 | print(file.peer_users)
249 | if not file.peer_users.filter(id=user.id).exists():
250 | file.peer_users.add(user)
251 | file.save()
252 | return Response(
253 | f"User added to file with id {file.id}", status=status.HTTP_200_OK
254 | )
255 | except Exception as e:
256 | print(f"Error occured while adding peer to file: {e}")
257 | return Response(
258 | "Unable to register peer to given file id",
259 | status=status.HTTP_400_BAD_REQUEST,
260 | )
261 |
262 |
263 | class FileFilterView(APIView):
264 | authentication_classes = [TokenAuthentication]
265 | permission_classes = [IsAuthenticated]
266 |
267 | def get(self, request, format=None):
268 | try:
269 | # Extract filters from the query parameters
270 | # update_user_ip(request)
271 | topic_id = request.query_params.get("topic")
272 | professor_id = request.query_params.get("professor")
273 | course_id = request.query_params.get("course")
274 | semester_id = request.query_params.get("semester")
275 |
276 | # Check if provided filter IDs exist
277 | if topic_id and not Topic.objects.filter(id=topic_id).exists():
278 | return Response(
279 | {"error": f"Topic with id {topic_id} does not exist"},
280 | status=status.HTTP_400_BAD_REQUEST,
281 | )
282 | if professor_id and not Professor.objects.filter(id=professor_id).exists():
283 | return Response(
284 | {"error": f"Professor with id {professor_id} does not exist"},
285 | status=status.HTTP_400_BAD_REQUEST,
286 | )
287 | if course_id and not Course.objects.filter(id=course_id).exists():
288 | return Response(
289 | {"error": f"Course with id {course_id} does not exist"},
290 | status=status.HTTP_400_BAD_REQUEST,
291 | )
292 | if semester_id and not Semester.objects.filter(id=semester_id).exists():
293 | return Response(
294 | {"error": f"Semester with id {semester_id} does not exist"},
295 | status=status.HTTP_400_BAD_REQUEST,
296 | )
297 |
298 | # Start with the base queryset
299 | queryset = File.objects.all()
300 |
301 | # Apply filters if they are provided
302 | if topic_id:
303 | queryset = queryset.filter(topic__id=topic_id)
304 | if professor_id:
305 | queryset = queryset.filter(professor__id=professor_id)
306 | if course_id:
307 | queryset = queryset.filter(course__id=course_id)
308 | if semester_id:
309 | queryset = queryset.filter(semester__id=semester_id)
310 |
311 | queryset = queryset.annotate(
312 | upvote_count=Count("upvotes"), downvote_count=Count("downvotes")
313 | ).order_by(F("downvote_count") - F("upvote_count"))
314 |
315 | # Filter files based on active peers in the past hour
316 | active_peer_ids = PeerUser.objects.filter(
317 | last_poll__gte=timezone.now() - timedelta(hours=1)
318 | ).values_list("id", flat=True)
319 | queryset = queryset.filter(peer_users__in=active_peer_ids).distinct()
320 |
321 | serializer = FileSerializer(queryset, many=True)
322 | # Loop through each serialized file to sort its peer_users
323 |
324 | try:
325 | serialized_data = serializer.data
326 | print(serialized_data)
327 | except Exception as e:
328 | print(str(e))
329 | for file_data in serialized_data:
330 | print(file_data)
331 | file_obj = File.objects.get(id=file_data["id"])
332 | sorted_peer_users = sorted(
333 | file_obj.peer_users.all(), key=lambda x: x.points, reverse=True
334 | )
335 | file_data["peer_users"] = UserSerializer(
336 | sorted_peer_users, many=True
337 | ).data
338 |
339 | return Response(serialized_data)
340 | except Exception as e:
341 | return Response(
342 | {"error": f"Something went wrong: {str(e)}"},
343 | status=status.HTTP_500_INTERNAL_SERVER_ERROR,
344 | )
345 |
346 |
347 | class UserFilesView(APIView):
348 | authentication_classes = [TokenAuthentication]
349 | permission_classes = [IsAuthenticated]
350 |
351 | def get(self, request):
352 | user = request.user
353 |
354 | owned_files = user.owned_files.all()
355 | shared_files = user.shared_files.all()
356 |
357 | all_user_files = owned_files.union(shared_files)
358 |
359 | serializer = FileSerializer(all_user_files, many=True)
360 |
361 | try:
362 | return Response(serializer.data, status=status.HTTP_200_OK)
363 | except Exception as e:
364 | print(f"Error: {e}")
365 | return Response(
366 | {"error": f"An error occured {e}"}, status=status.HTTP_400_BAD_REQUEST
367 | )
368 |
369 |
370 | class ReportUserView(APIView):
371 | authentication_classes = [TokenAuthentication]
372 | permission_classes = [IsAuthenticated]
373 |
374 | def post(self, request):
375 | data = request.data
376 | # update_user_ip(request)
377 | try:
378 | assert "file_id" in data
379 | assert "user_id" in data
380 | assert "description" in data
381 | except AssertionError as e:
382 | return Response(
383 | {"error": f"Missing Parameters. {str(e)}"},
384 | status=status.HTTP_400_BAD_REQUEST,
385 | )
386 |
387 | try:
388 | file = File.objects.get(id=data["file_id"])
389 | malicious_user = PeerUser.objects.get(id=data["user_id"])
390 | reporting_user = request.user
391 | description = data["description"]
392 | except Exception as e:
393 | return Response(
394 | {"error": f"Missing/Incorrect Parameters. {str(e)}"},
395 | status=status.HTTP_400_BAD_REQUEST,
396 | )
397 |
398 | existing_user_report_queryset = UserReport.objects.filter(
399 | user=malicious_user, file=file, reporting_user=reporting_user
400 | )
401 |
402 | if len(existing_user_report_queryset):
403 | existing_report = existing_user_report_queryset.first()
404 | existing_report.description = description
405 | existing_report.save()
406 | return Response(
407 | {
408 | "msg": "User report already exists for this file. Updated description"
409 | },
410 | status=status.HTTP_208_ALREADY_REPORTED,
411 | )
412 |
413 | if (
414 | file not in malicious_user.owned_files.all()
415 | or file not in malicious_user.shared_files.all()
416 | ):
417 | return Response(
418 | {"error": "User does not own or host the file reported"},
419 | status=status.HTTP_404_NOT_FOUND,
420 | )
421 |
422 | UserReport.objects.create(
423 | user=malicious_user,
424 | reporting_user=reporting_user,
425 | file=file,
426 | description=description,
427 | )
428 | malicious_user.points -= 1
429 | malicious_user.save()
430 | return Response({"msg": "Report Created!"}, status=status.HTTP_201_CREATED)
431 |
--------------------------------------------------------------------------------
/backend/api/views/filters.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmanTahiliani/PeerNotes/46a4574f62d46a0110f9324f7d0e23b3c8f3f9bc/backend/api/views/filters.py
--------------------------------------------------------------------------------
/backend/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 | import os
4 | import sys
5 |
6 |
7 | def main():
8 | """Run administrative tasks."""
9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "peer_notes.settings")
10 | try:
11 | from django.core.management import execute_from_command_line
12 | except ImportError as exc:
13 | raise ImportError(
14 | "Couldn't import Django. Are you sure it's installed and "
15 | "available on your PYTHONPATH environment variable? Did you "
16 | "forget to activate a virtual environment?"
17 | ) from exc
18 | execute_from_command_line(sys.argv)
19 |
20 |
21 | if __name__ == "__main__":
22 | main()
23 |
--------------------------------------------------------------------------------
/backend/peer_notes/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmanTahiliani/PeerNotes/46a4574f62d46a0110f9324f7d0e23b3c8f3f9bc/backend/peer_notes/__init__.py
--------------------------------------------------------------------------------
/backend/peer_notes/asgi.py:
--------------------------------------------------------------------------------
1 | """
2 | ASGI config for peer_notes project.
3 |
4 | It exposes the ASGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.asgi import get_asgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "peer_notes.settings")
15 |
16 | application = get_asgi_application()
17 |
--------------------------------------------------------------------------------
/backend/peer_notes/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for peer_notes project.
3 |
4 | Generated by 'django-admin startproject' using Django 4.2.11.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/4.2/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/4.2/ref/settings/
11 | """
12 |
13 | from pathlib import Path
14 |
15 | # Build paths inside the project like this: BASE_DIR / 'subdir'.
16 | BASE_DIR = Path(__file__).resolve().parent.parent
17 |
18 |
19 | # Quick-start development settings - unsuitable for production
20 | # See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = "django-insecure-rh0s*u8$uugtt10cxbifjriaz%@&p1w!)c2=y^undd2*nx5"
24 |
25 | # SECURITY WARNING: don't run with debug turned on in production!
26 | DEBUG = True
27 |
28 | ALLOWED_HOSTS = ["*"]
29 | # ALLOWED_HOSTS = []
30 |
31 |
32 | # Application definition
33 |
34 | INSTALLED_APPS = [
35 | "django.contrib.admin",
36 | "django.contrib.auth",
37 | "django.contrib.contenttypes",
38 | "django.contrib.sessions",
39 | "django.contrib.messages",
40 | "django.contrib.staticfiles",
41 | "rest_framework",
42 | "api.apps.ApiConfig",
43 | "rest_framework.authtoken",
44 | "corsheaders",
45 | ]
46 |
47 | REST_FRAMEWORK = {
48 | "DEFAULT_AUTHENTICATION_CLASSES": [
49 | "rest_framework.authentication.TokenAuthentication",
50 | ],
51 | }
52 |
53 | MIDDLEWARE = [
54 | "corsheaders.middleware.CorsMiddleware",
55 | "django.middleware.security.SecurityMiddleware",
56 | "django.contrib.sessions.middleware.SessionMiddleware",
57 | "django.middleware.common.CommonMiddleware",
58 | "django.middleware.csrf.CsrfViewMiddleware",
59 | "django.contrib.auth.middleware.AuthenticationMiddleware",
60 | "django.contrib.messages.middleware.MessageMiddleware",
61 | "django.middleware.clickjacking.XFrameOptionsMiddleware",
62 | ]
63 |
64 | ROOT_URLCONF = "peer_notes.urls"
65 |
66 | TEMPLATES = [
67 | {
68 | "BACKEND": "django.template.backends.django.DjangoTemplates",
69 | "DIRS": [],
70 | "APP_DIRS": True,
71 | "OPTIONS": {
72 | "context_processors": [
73 | "django.template.context_processors.debug",
74 | "django.template.context_processors.request",
75 | "django.contrib.auth.context_processors.auth",
76 | "django.contrib.messages.context_processors.messages",
77 | ],
78 | },
79 | },
80 | ]
81 |
82 | WSGI_APPLICATION = "peer_notes.wsgi.application"
83 |
84 |
85 | # Database
86 | # https://docs.djangoproject.com/en/4.2/ref/settings/#databases
87 |
88 | DATABASES = {
89 | "default": {
90 | "ENGINE": "django.db.backends.sqlite3",
91 | "NAME": BASE_DIR / "db.sqlite3",
92 | }
93 | }
94 |
95 |
96 | # Password validation
97 | # https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
98 |
99 | AUTH_PASSWORD_VALIDATORS = [
100 | {
101 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
102 | },
103 | {
104 | "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
105 | },
106 | {
107 | "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
108 | },
109 | {
110 | "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
111 | },
112 | ]
113 |
114 |
115 | # Internationalization
116 | # https://docs.djangoproject.com/en/4.2/topics/i18n/
117 |
118 | LANGUAGE_CODE = "en-us"
119 |
120 | TIME_ZONE = "UTC"
121 |
122 | USE_I18N = True
123 |
124 | USE_TZ = True
125 |
126 |
127 | # Static files (CSS, JavaScript, Images)
128 | # https://docs.djangoproject.com/en/4.2/howto/static-files/
129 |
130 | STATIC_URL = "static/"
131 |
132 | # Default primary key field type
133 | # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
134 |
135 | DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
136 | AUTH_USER_MODEL = "api.PeerUser"
137 | CORS_ALLOW_ALL_ORIGINS = True
138 | CORS_ALLOW_ALL_METHODS = True
139 | # CORS_ALLOWED_ORIGINS = [
140 | # "http://localhost:5173"
141 | # ]
142 | CORS_ALLOW_CREDENTIALS = True
143 |
--------------------------------------------------------------------------------
/backend/peer_notes/urls.py:
--------------------------------------------------------------------------------
1 | """
2 | URL configuration for peer_notes project.
3 |
4 | The `urlpatterns` list routes URLs to views. For more information please see:
5 | https://docs.djangoproject.com/en/4.2/topics/http/urls/
6 | Examples:
7 | Function views
8 | 1. Add an import: from my_app import views
9 | 2. Add a URL to urlpatterns: path('', views.home, name='home')
10 | Class-based views
11 | 1. Add an import: from other_app.views import Home
12 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
13 | Including another URLconf
14 | 1. Import the include() function: from django.urls import include, path
15 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
16 | """
17 |
18 | from django.contrib import admin
19 | from django.urls import path, include
20 |
21 | urlpatterns = [
22 | path("admin/", admin.site.urls),
23 | path("", include("api.urls")),
24 | ]
25 |
--------------------------------------------------------------------------------
/backend/peer_notes/views.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmanTahiliani/PeerNotes/46a4574f62d46a0110f9324f7d0e23b3c8f3f9bc/backend/peer_notes/views.py
--------------------------------------------------------------------------------
/backend/peer_notes/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for peer_notes project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "peer_notes.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/backend/requirements.txt:
--------------------------------------------------------------------------------
1 | asgiref==3.8.1
2 | black==24.2.0
3 | blinker==1.7.0
4 | click==8.1.7
5 | Django==4.2.11
6 | django-cors-headers==4.3.1
7 | djangorestframework==3.15.1
8 | Flask==3.0.2
9 | importlib-metadata==7.0.1
10 | itsdangerous==2.1.2
11 | Jinja2==3.1.3
12 | MarkupSafe==2.1.5
13 | mypy-extensions==1.0.0
14 | packaging==23.2
15 | pathspec==0.12.1
16 | platformdirs==4.2.0
17 | sqlparse==0.4.4
18 | tomli==2.0.1
19 | typing_extensions==4.10.0
20 | Werkzeug==3.0.1
21 | zipp==3.17.0
22 | flask
23 | flask-cors
--------------------------------------------------------------------------------
/backend/server.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, request
2 | import signal
3 |
4 | app = Flask(__name__)
5 | file_index = {}
6 |
7 |
8 | def shutdown_server(signal, frame):
9 | print("Shutting down server...")
10 | # Close the Flask app context
11 | ctx = app.app_context()
12 | ctx.push()
13 | # Shutdown the Flask server
14 | app.shutdown()
15 | ctx.pop()
16 |
17 |
18 | # Register the signal handler for SIGINT (keyboard interrupt)
19 | signal.signal(signal.SIGINT, shutdown_server)
20 |
21 |
22 | @app.route("/register", methods=["POST"])
23 | def register_node():
24 | data = request.json
25 | node_ip = data["ip"]
26 | files = data["files"]
27 | for file in files:
28 | if file not in file_index:
29 | file_index[file] = []
30 | file_index[file].append(node_ip)
31 | print(file_index)
32 | return "Node registered successfully", 200
33 |
34 |
35 | @app.route("/query", methods=["GET"])
36 | def query_file():
37 | file_name = request.args.get("file")
38 | print("Requested File Name: ", file_name)
39 | if file_name in file_index:
40 | return {"nodes": file_index[file_name]}, 200
41 | else:
42 | return "File not found", 404
43 |
44 |
45 | if __name__ == "__main__":
46 | app.run(host="0.0.0.0", port=8000)
47 |
--------------------------------------------------------------------------------
/file_peer/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 | uploads/
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # poetry
98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99 | # This is especially recommended for binary packages to ensure reproducibility, and is more
100 | # commonly ignored for libraries.
101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102 | #poetry.lock
103 |
104 | # pdm
105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106 | #pdm.lock
107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108 | # in version control.
109 | # https://pdm.fming.dev/#use-with-ide
110 | .pdm.toml
111 |
112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113 | __pypackages__/
114 |
115 | # Celery stuff
116 | celerybeat-schedule
117 | celerybeat.pid
118 |
119 | # SageMath parsed files
120 | *.sage.py
121 |
122 | # Environments
123 | .env
124 | .venv
125 | env/
126 | venv/
127 | ENV/
128 | env.bak/
129 | venv.bak/
130 |
131 | # Spyder project settings
132 | .spyderproject
133 | .spyproject
134 |
135 | # Rope project settings
136 | .ropeproject
137 |
138 | # mkdocs documentation
139 | /site
140 |
141 | # mypy
142 | .mypy_cache/
143 | .dmypy.json
144 | dmypy.json
145 |
146 | # Pyre type checker
147 | .pyre/
148 |
149 | # pytype static type analyzer
150 | .pytype/
151 |
152 | # Cython debug symbols
153 | cython_debug/
154 |
155 | # PyCharm
156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158 | # and can be added to the global gitignore or merged into this file. For a more nuclear
159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160 | #.idea/
161 |
162 | # Misc
163 | .DS_STORE
164 |
--------------------------------------------------------------------------------
/file_peer/peer_service.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, send_file, request
2 | import requests
3 | import socket
4 | import shutil
5 | import os
6 |
7 | from flask_cors import CORS
8 |
9 | app = Flask(__name__)
10 |
11 | # Enable CORS for all routes
12 | CORS(app)
13 |
14 |
15 | @app.route("/copy-file", methods=["POST"])
16 | def copy_file():
17 | try:
18 | files = request.files
19 | id = request.form["file_id"]
20 | print(id)
21 | if "file" not in files:
22 | return "File must be sent in request", 400
23 | file = request.files["file"]
24 | filename = file.filename.replace(" ", "_")
25 | # id = data["file_id"]
26 | # if not os.path.exists(source_file):
27 | # return f"Source file does not exist at {source_file}", 400
28 | # filename = os.path.basename(source_file)
29 | destination_path = os.path.join("./uploads/", str(id) + "_" + filename)
30 | # shutil.copyfile(source_file, destination_path)
31 | if os.path.exists(destination_path):
32 | return "File with name already exists", 200
33 |
34 | with open(destination_path, "wb") as f:
35 | f.write(file.read())
36 | return "File Moved successfully", 200
37 | except Exception as e:
38 | print(f"An error occured: {e}")
39 | return "An error occured", 400
40 |
41 |
42 | @app.route("/ip", methods=["GET"])
43 | def get_internal_ip():
44 | internal_ip = None
45 | try:
46 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
47 | s.connect(("8.8.8.8", 80))
48 | internal_ip = s.getsockname()[0]
49 | s.close()
50 | except Exception as e:
51 | print("Error:", e)
52 | return internal_ip, 200
53 |
54 |
55 | @app.route("/send", methods=["GET"])
56 | def send():
57 | file_id = request.args.get("id")
58 | filename = request.args.get("filename")
59 |
60 | file_path = "./uploads/" + file_id + "_" + filename
61 |
62 | return send_file(
63 | file_path, as_attachment=True, download_name=str(file_id) + "_" + filename
64 | )
65 |
66 |
67 | @app.route("/request", methods=["POST"])
68 | def request_file():
69 | data = request.json
70 | file_id = data["id"]
71 | filename = data["filename"]
72 | ip = data["ip"]
73 |
74 | url = f"http://{ip}:8080/send"
75 | params = {
76 | "id": file_id,
77 | "filename": filename.replace(" ", "_"),
78 | }
79 |
80 | response = requests.get(url, params=params)
81 | file_path = "./uploads/" + str(file_id) + "_" + filename
82 |
83 | if response.status_code == 200:
84 | if os.path.exists(file_path):
85 | return "File with name already exists", 200
86 |
87 | with open(file_path, "wb") as f:
88 | f.write(response.content)
89 | print("File downloaded successfully")
90 | return f"File Downloaded to location to location {file_path}", 200
91 | else:
92 | error = response.text
93 | print("Error:", error)
94 | return error, response.status_code
95 |
96 |
97 | @app.route("/files", methods=["GET"])
98 | def list_files():
99 | files = os.listdir("./uploads")
100 | newFiles = []
101 | for file in files:
102 | newFiles.append("_".join(file.split("_", 1)[1:]))
103 | return {"files": newFiles}, 200
104 |
105 |
106 | @app.route("/request-tests", methods=["GET"])
107 | def request_test_():
108 | data = request.json
109 | file_id = data["id"]
110 | filename = data["filename"]
111 | ip = data["ip"]
112 | new_filename = data["new_filename"]
113 |
114 | url = f"http://{ip}:8080/send"
115 | params = {
116 | "id": file_id,
117 | "filename": filename,
118 | }
119 |
120 | response = requests.get(url, params=params)
121 | file_path = "./uploads/" + str(file_id) + "_" + new_filename
122 |
123 | if response.status_code == 200:
124 | if os.path.exists(file_path):
125 | return "File with name already exists", 200
126 |
127 | with open(file_path, "wb") as f:
128 | f.write(response.content)
129 | print("File downloaded successfully")
130 | return f"File Downloaded to location to location {file_path}", 200
131 | else:
132 | error = response.text
133 | print("Error:", error)
134 | return error, response.status_code
135 |
136 |
137 | if __name__ == "__main__":
138 | app.run(debug=True, host="0.0.0.0", port=8080)
139 |
--------------------------------------------------------------------------------
/file_peer/uploads/1_abcd.txt:
--------------------------------------------------------------------------------
1 | Hello World!
2 | Flask Notes Transfer Test
--------------------------------------------------------------------------------
/file_peer/uploads/2_bcdef.txt:
--------------------------------------------------------------------------------
1 | Hello World!
2 | Flask Notes Transfer Test 2
--------------------------------------------------------------------------------
/frontend/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: { browser: true, es2020: true },
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:@typescript-eslint/recommended',
7 | 'plugin:react-hooks/recommended',
8 | ],
9 | ignorePatterns: ['dist', '.eslintrc.cjs'],
10 | parser: '@typescript-eslint/parser',
11 | plugins: ['react-refresh'],
12 | rules: {
13 | 'react-refresh/only-export-components': [
14 | 'warn',
15 | { allowConstantExport: true },
16 | ],
17 | },
18 | }
19 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | # React + TypeScript + Vite
2 |
3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4 |
5 | Currently, two official plugins are available:
6 |
7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9 |
10 | ## Expanding the ESLint configuration
11 |
12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
13 |
14 | - Configure the top-level `parserOptions` property like this:
15 |
16 | ```js
17 | export default {
18 | // other rules...
19 | parserOptions: {
20 | ecmaVersion: 'latest',
21 | sourceType: 'module',
22 | project: ['./tsconfig.json', './tsconfig.node.json'],
23 | tsconfigRootDir: __dirname,
24 | },
25 | }
26 | ```
27 |
28 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
29 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
30 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
31 |
--------------------------------------------------------------------------------
/frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | PeerNotes
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0",
15 | "react-router-dom": "^6.22.3"
16 | },
17 | "devDependencies": {
18 | "@types/react": "^18.2.64",
19 | "@types/react-dom": "^18.2.21",
20 | "@typescript-eslint/eslint-plugin": "^7.1.1",
21 | "@typescript-eslint/parser": "^7.1.1",
22 | "@vitejs/plugin-react-swc": "^3.5.0",
23 | "eslint": "^8.57.0",
24 | "eslint-plugin-react-hooks": "^4.6.0",
25 | "eslint-plugin-react-refresh": "^0.4.5",
26 | "typescript": "^5.2.2",
27 | "vite": "^5.1.6"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/frontend/public/PeerNotes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmanTahiliani/PeerNotes/46a4574f62d46a0110f9324f7d0e23b3c8f3f9bc/frontend/public/PeerNotes.png
--------------------------------------------------------------------------------
/frontend/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/App.tsx:
--------------------------------------------------------------------------------
1 | import "./styles/App.css";
2 | import { Link, useNavigate } from 'react-router-dom';
3 | import { Outlet } from "react-router-dom";
4 | import { SessionContext, destroySessionCookie, getSessionCookie } from "./contexts/session";
5 | import { useState, useEffect, useContext } from "react";
6 | import { usePoll } from "./hooks/usePoll";
7 |
8 | export default function App() {
9 | const [session, setSession] = useState(getSessionCookie());
10 | const navigate = useNavigate();
11 | usePoll();
12 | // Redirect to login if session is undefined
13 | useEffect(
14 | () => {
15 | setSession(getSessionCookie());
16 | if (session === null) {
17 | navigate("/login");
18 | }
19 | },
20 | [session, navigate]
21 | );
22 |
23 | return (
24 | <>
25 |
26 |
27 |
28 |
29 |
30 |
31 | >
32 | )
33 | }
34 | function Navbar() {
35 | const linkStyle = {
36 | textDecoration: 'none',
37 | color: 'black',
38 | background: '#B1D4E0',
39 | padding: '8px 16px',
40 | borderRadius: '5px',
41 | // margin: '0 5px'
42 | };
43 | const session = useContext(SessionContext);
44 | const logout = () => {
45 | destroySessionCookie();
46 | window.location.reload();
47 | }
48 |
49 | const links = session ? (
50 | <>
51 |
52 |
53 |
Upload
54 |
Search
55 |
56 |
59 | >
60 | ): (
61 | Login
62 | )
63 | return (
64 |
65 | {links}
66 |
67 | );
68 | }
--------------------------------------------------------------------------------
/frontend/src/assets/file.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/frontend/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/contexts/session.ts:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | type SessionContextType = string | null;
4 |
5 | export const getSessionCookie = () => {
6 | // const sessionCookie = document.cookie
7 | // .split('; ')
8 | // .find(row => row.startsWith('token='))
9 | // ?.split('=')[1];
10 | const sessionCookie = localStorage.getItem('authToken');
11 | return sessionCookie;
12 | }
13 | export const destroySessionCookie = () => {
14 | console.log("destroying session cookie");
15 | localStorage.removeItem('authToken');
16 | // document.cookie = "token=; Max-Age=0;"
17 | }
18 |
19 | export const SessionContext = React.createContext(getSessionCookie());
20 |
21 |
--------------------------------------------------------------------------------
/frontend/src/hooks/sessionRedirect.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useContext } from "react";
2 | import { useNavigate } from "react-router-dom";
3 | import { SessionContext } from "../contexts/session";
4 |
5 | export const useSessionRedirect = () => {
6 | const session = useContext(SessionContext);
7 | const navigate = useNavigate();
8 | // Redirect to home if session is defined
9 | useEffect(() => {
10 | if (session !== null) {
11 | navigate('/search');
12 | }
13 | }, [session, navigate])
14 | }
--------------------------------------------------------------------------------
/frontend/src/hooks/usePoll.ts:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react"
2 | import { getAuthHeaders } from "../utils/getAuthHeaders"
3 | export const usePoll = () => {
4 | useEffect(() => {
5 | const interval = setInterval(() => {
6 | pollServer()
7 | .catch((error) => {
8 | console.error('Error:', error);
9 | });
10 | }, 30 * 60000) // poll every 30 minutes
11 | return () => {
12 | // clean up
13 | clearInterval(interval);
14 | };
15 | }, [])
16 |
17 | const pollServer = async () => {
18 | // request private ip from local api
19 | return fetch('http://localhost:8080/ip')
20 | .then(response => response.text())
21 | .then((data) => {
22 | // send private ip to central server
23 | return fetch(`${import.meta.env.VITE_CENTRAL_SERVER}/api/poll/`, {
24 | method: 'POST',
25 | headers: getAuthHeaders(),
26 | body: JSON.stringify({ip: data}),
27 | })
28 | }).then(response => response.json())
29 | .then(data => {
30 | console.log("Poll response data:", data);
31 | })
32 | }
33 | }
--------------------------------------------------------------------------------
/frontend/src/index.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmanTahiliani/PeerNotes/46a4574f62d46a0110f9324f7d0e23b3c8f3f9bc/frontend/src/index.css
--------------------------------------------------------------------------------
/frontend/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 | import {
6 | createBrowserRouter,
7 | RouterProvider,
8 | } from "react-router-dom";
9 | import Login from './screens/Login.tsx';
10 | import Signup from './screens/Signup.tsx';
11 | import Results from './screens/Results.tsx';
12 | import MainSearch from './screens/MainSearch.tsx';
13 | import RegisterFile from './screens/RegisterFile.tsx';
14 |
15 | const router = createBrowserRouter([
16 | {
17 | path: "/",
18 | element: ,
19 | children: [
20 | // add more routes here
21 | {
22 | path: "search",
23 | element: ,
24 | },
25 | {
26 | path: "results",
27 | element: ,
28 | },
29 | {
30 | path: "register",
31 | element: ,
32 | },
33 | ]
34 | },
35 |
36 | {
37 | path: "/login",
38 | element: ,
39 | },
40 | {
41 | path: "/signup",
42 | element: ,
43 | },
44 | ]);
45 |
46 | ReactDOM.createRoot(document.getElementById('root')!).render(
47 |
48 |
49 | ,
50 | )
51 |
--------------------------------------------------------------------------------
/frontend/src/screens/Login.tsx:
--------------------------------------------------------------------------------
1 | import { Link, useNavigate } from "react-router-dom"
2 | import "../styles/Login.css"
3 | import { FormEvent } from "react";
4 | import { useSessionRedirect } from "../hooks/sessionRedirect";
5 |
6 | export default function Login() {
7 | const navigate = useNavigate();
8 |
9 | useSessionRedirect();
10 |
11 | function handleSubmit(e: FormEvent) {
12 | e.preventDefault();
13 | const formData = new FormData(e.target as HTMLFormElement);
14 | const username = formData.get('username');
15 | const password = formData.get('password');
16 | fetch(`${import.meta.env.VITE_CENTRAL_SERVER}/api/login/`, {
17 | method: 'POST',
18 | credentials: 'include',
19 | headers: {
20 | 'Content-Type': 'application/json',
21 | },
22 | body: JSON.stringify({ username, password }),
23 | })
24 | .then(response => response.json())
25 | .then(data => {
26 | console.log("Login response data:", data);
27 | if (data.token) {
28 | localStorage.setItem('authToken', data.token);
29 | navigate('/search');
30 | } else {
31 | console.error('No token received:', data);
32 | }
33 | })
34 | .catch((error) => {
35 | console.error('Error:', error);
36 | });
37 | }
38 | return (
39 |
40 |
53 |
54 | );
55 | }
--------------------------------------------------------------------------------
/frontend/src/screens/MainSearch.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { useNavigate } from 'react-router-dom';
3 | import '../styles/MainScreenWrapper.css';
4 | import { getAuthHeaders } from '../utils/getAuthHeaders';
5 | import { Professor, Course, Topic, Semester } from "../types/types";
6 | import { getSessionCookie } from '../contexts/session';
7 |
8 | interface Filters extends Record {
9 | professor: string;
10 | course: string;
11 | topic: string;
12 | }
13 |
14 | const MainSearch: React.FC = () => {
15 | const [filters, setFilters] = useState({
16 | professor: '',
17 | course: '',
18 | topic: '',
19 | });
20 | const [professors, setProfessors] = useState([]);
21 | const [courses, setCourses] = useState([]);
22 | const [topics, setTopics] = useState([]);
23 | const [semesters, setSemesters] = useState([]);
24 | const navigate = useNavigate();
25 |
26 | useEffect(() => {
27 | if (!getSessionCookie()) {
28 | return;
29 | }
30 | // Fetch professors
31 | fetchProfessors();
32 | // Fetch courses
33 | fetchCourses();
34 | // Fetch topics
35 | fetchTopics();
36 | // Fetch semesters
37 | fetchSemesters();
38 | }, []);
39 |
40 |
41 | const fetchProfessors = async () => {
42 | const token = getSessionCookie();
43 | try {
44 | const response = await fetch(`${import.meta.env.VITE_CENTRAL_SERVER}/api/professors/`, {
45 | method: 'GET',
46 | headers: {
47 | 'Authorization': `Token ${token}`,
48 | },
49 | });
50 | const data = await response.json();
51 | console.log("HEREEEEEE", data);
52 | console.log(response)
53 | if (Array.isArray(data)) {
54 | setProfessors(data);
55 | } else {
56 | console.error('Data fetched is not an array:', data);
57 | }
58 | } catch (error) {
59 | console.error('Error fetching professors:', error);
60 | }
61 | };
62 |
63 | const fetchCourses = async () => {
64 | try {
65 | const response = await fetch(`${import.meta.env.VITE_CENTRAL_SERVER}/api/courses`, {
66 | method: 'GET',
67 | headers: getAuthHeaders(),
68 | });
69 | const data = await response.json();
70 | if (Array.isArray(data)) {
71 | setCourses(data);
72 | } else {
73 | console.error('Data fetched is not an array:', data);
74 | }
75 | } catch (error) {
76 | console.error('Error fetching courses:', error);
77 | }
78 | };
79 |
80 | const fetchTopics = async () => {
81 | try {
82 | const response = await fetch(`${import.meta.env.VITE_CENTRAL_SERVER}/api/topics`, {
83 | method: 'GET',
84 | headers: getAuthHeaders(),
85 | });
86 | const data = await response.json();
87 | if (Array.isArray(data)) {
88 | setTopics(data);
89 | } else {
90 | console.error('Data fetched is not an array:', data);
91 | }
92 | } catch (error) {
93 | console.error('Error fetching topics:', error);
94 | }
95 | };
96 | const fetchSemesters = async () => {
97 | try {
98 | const response = await fetch(`${import.meta.env.VITE_CENTRAL_SERVER}/api/semesters`, {
99 | method: 'GET',
100 | headers: getAuthHeaders(),
101 | });
102 | const data = await response.json();
103 | if (Array.isArray(data)) {
104 | setSemesters(data);
105 | } else {
106 | console.error('Data fetched is not an array:', data);
107 | }
108 | } catch (error) {
109 | console.error('Error fetching courses:', error);
110 | }
111 | };
112 |
113 | const handleChange = (event: React.ChangeEvent) => {
114 | const { name, value } = event.target;
115 | setFilters((prevFilters: Filters) => ({
116 | ...prevFilters,
117 | [name]: value,
118 | }));
119 | };
120 |
121 | const handleSubmit = (event: React.FormEvent) => {
122 | event.preventDefault();
123 | const searchParams = new URLSearchParams(filters);
124 | navigate(`/results?${searchParams}`);
125 | };
126 |
127 | return (
128 | <>
129 |
130 |
169 | >
170 | );
171 | }
172 |
173 | export default MainSearch;
174 |
--------------------------------------------------------------------------------
/frontend/src/screens/RegisterFile.tsx:
--------------------------------------------------------------------------------
1 | import "../styles/RegisterFile.css";
2 | import { RegisteredFile, Status, Professor, Course, Topic, Semester } from "../types/types";
3 | import { useState, useEffect } from "react";
4 | import { getAuthHeaders } from "../utils/getAuthHeaders";
5 | import { getSessionCookie } from "../contexts/session";
6 |
7 | export default function RegisterFile() {
8 | const [serverFiles, setServerFiles] = useState([]);
9 | const [registeredFiles, setRegisteredFiles] = useState([]);
10 |
11 | useEffect(() => {
12 | fetchServerFiles();
13 | }, [])
14 |
15 | useEffect(() => {
16 | fetch(`http://localhost:8080/files`, {
17 | method: 'GET',
18 | })
19 | .then(response => response.json())
20 | .then(data => {
21 | const files: string[] = data.files.map((file: string) => {
22 | return file.replace(/\s/g, "_");
23 | })
24 | setRegisteredFiles(
25 | serverFiles.map((file) => {
26 | if (files.includes(file.filename)) {
27 | file.status = Status.HOSTED;
28 | return file;
29 | }
30 | file.status = Status.PRIVATE;
31 | return file;
32 | })
33 | )
34 | })
35 | }, [serverFiles])
36 |
37 | const fetchServerFiles = () => {
38 | fetch(`${import.meta.env.VITE_CENTRAL_SERVER}/api/get-peer-files/`, {
39 | method: 'GET',
40 | headers: getAuthHeaders()
41 | })
42 | .then(response => response.json())
43 | .then(data => {
44 | setServerFiles(data)
45 | })
46 | .catch(error => console.error(error));
47 | }
48 |
49 | const handleSubmit = (event: React.FormEvent) => {
50 | event.preventDefault();
51 | // handle file upload
52 | const fileInput = document.getElementById('file');
53 | // @ts-expect-error - fileInput is an HTMLInputElement
54 | const filename: string = fileInput.files[0].name.replace(/\s/g, "_");
55 | const formData = new FormData(event.target as HTMLFormElement);
56 |
57 | // register file with central server
58 | fetch(`${import.meta.env.VITE_CENTRAL_SERVER}/api/register/`, {
59 | method: 'POST',
60 | headers: getAuthHeaders(),
61 | body: JSON.stringify({
62 | filename: filename,
63 | topic: formData.get('topic'),
64 | semester: formData.get('semester'),
65 | professor: formData.get('professor'),
66 | course: formData.get('course'),
67 | })
68 | })
69 | .then(response => response.json())
70 | .then(data => {
71 | formData.append("file_id", data.id)
72 | // send file to local
73 | return fetch("http://localhost:8080/copy-file", {
74 | method: 'POST',
75 | body: formData
76 | })
77 | })
78 | .then(response => response.text())
79 | .then(data => {
80 | console.log(data);
81 | window.location.reload();
82 | })
83 | .catch(error => console.error(error));
84 | }
85 |
86 | // Filters Logic
87 | interface Filters extends Record {
88 | professor: string;
89 | course: string;
90 | topic: string;
91 | }
92 | const [filters, setFilters] = useState({
93 | professor: '',
94 | course: '',
95 | topic: '',
96 | });
97 | const [professors, setProfessors] = useState([]);
98 | const [courses, setCourses] = useState([]);
99 | const [topics, setTopics] = useState([]);
100 | const [semesters, setSemesters] = useState([]);
101 |
102 | useEffect(() => {
103 | if (!getSessionCookie()) {
104 | return;
105 | }
106 | // Fetch professors
107 | fetchProfessors();
108 | // Fetch courses
109 | fetchCourses();
110 | // Fetch topics
111 | fetchTopics();
112 | // Fetch semesters
113 | fetchSemesters();
114 | }, []);
115 |
116 |
117 | const fetchProfessors = async () => {
118 | try {
119 | const response = await fetch(`${import.meta.env.VITE_CENTRAL_SERVER}/api/professors`, {
120 | method: 'GET',
121 | headers: getAuthHeaders(),
122 | });
123 | const data = await response.json();
124 |
125 | if (Array.isArray(data)) {
126 | setProfessors(data);
127 | } else {
128 | console.error('Data fetched is not an array:', data);
129 | }
130 | } catch (error) {
131 | console.error('Error fetching professors:', error);
132 | }
133 | };
134 |
135 | const fetchCourses = async () => {
136 | try {
137 | const response = await fetch(`${import.meta.env.VITE_CENTRAL_SERVER}/api/courses`, {
138 | method: 'GET',
139 | headers: getAuthHeaders(),
140 | });
141 | const data = await response.json();
142 | if (Array.isArray(data)) {
143 | setCourses(data);
144 | } else {
145 | console.error('Data fetched is not an array:', data);
146 | }
147 | } catch (error) {
148 | console.error('Error fetching courses:', error);
149 | }
150 | };
151 |
152 | const fetchTopics = async () => {
153 | try {
154 | const response = await fetch(`${import.meta.env.VITE_CENTRAL_SERVER}/api/topics`, {
155 | method: 'GET',
156 | headers: getAuthHeaders(),
157 | });
158 | const data = await response.json();
159 | if (Array.isArray(data)) {
160 | setTopics(data);
161 | } else {
162 | console.error('Data fetched is not an array:', data);
163 | }
164 | } catch (error) {
165 | console.error('Error fetching topics:', error);
166 | }
167 | };
168 | const fetchSemesters = async () => {
169 | try {
170 | const response = await fetch(`${import.meta.env.VITE_CENTRAL_SERVER}/api/semesters`, {
171 | method: 'GET',
172 | headers: getAuthHeaders(),
173 | });
174 | const data = await response.json();
175 | if (Array.isArray(data)) {
176 | setSemesters(data);
177 | } else {
178 | console.error('Data fetched is not an array:', data);
179 | }
180 | } catch (error) {
181 | console.error('Error fetching courses:', error);
182 | }
183 | };
184 |
185 | const handleChange = (event: React.ChangeEvent) => {
186 | const { name, value } = event.target;
187 | setFilters((prevFilters: Filters) => ({
188 | ...prevFilters,
189 | [name]: value,
190 | }));
191 | };
192 | return (
193 |
194 |
195 |
Upload a File
196 |
Host a local file for the GT community!
197 |
238 |
239 |
240 |
Registered Files
241 |
242 |
243 |
244 | File Name
245 | Original Author
246 | Professor
247 | Semester
248 | Status
249 |
250 |
251 |
252 | {registeredFiles.map((file) => (
253 |
254 | {file.filename}
255 | {file.original_author.username}
256 | {file.professor.name}
257 | {file.semester.name}
258 | {file.status}
259 |
260 | ))}
261 |
262 |
263 |
264 |
265 | )
266 | }
--------------------------------------------------------------------------------
/frontend/src/screens/Results.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { useLocation, useNavigate } from 'react-router-dom';
3 | import styles from '../styles/Results.module.css'; // Make sure this path is correct
4 | import { getAuthHeaders } from '../utils/getAuthHeaders';
5 | import { File } from '../types/types';
6 | import { Link } from 'react-router-dom';
7 |
8 | const Results: React.FC = () => {
9 | const [files, setFiles] = useState([]);
10 | const [isLoading, setLoading] = useState(false);
11 | const location = useLocation();
12 | const navigate = useNavigate();
13 |
14 | useEffect(() => {
15 | const fetchFiles = async () => {
16 | setLoading(true);
17 | try {
18 | // Need to double check this
19 | const queryString = location.search; // Includes the '?' prefix
20 | console.log(queryString)
21 | const response = await fetch(`${import.meta.env.VITE_CENTRAL_SERVER}/api/files/filter${queryString}`, {headers: getAuthHeaders()});
22 | if (!response.ok) {
23 | throw new Error(`HTTP error! status: ${response.status}`);
24 | }
25 | const data = await response.json();
26 | setFiles(data);
27 | } catch (e) {
28 | console.error("Could not fetch files", e);
29 | } finally {
30 | setLoading(false);
31 | }
32 | };
33 |
34 | fetchFiles();
35 | }, [location]);
36 |
37 | const handleSearchAgain = () => {
38 | navigate('/search');
39 | };
40 |
41 | if (isLoading) return Loading...
;
42 |
43 | return (
44 |
45 |
46 |
47 |
Search Again
48 |
49 |
50 | {isLoading ? (
51 |
Loading...
52 | ) : files.length ? (
53 |
54 |
55 |
56 | Filename
57 | Course
58 | Professor
59 | Semester
60 | Upvotes
61 | Downvotes
62 | Original Author
63 | Download
64 |
65 |
66 |
67 | {files.map((file) => (
68 |
69 | {file.filename}
70 | {file.course.name} {file.course.number}
71 | {file.professor.name}
72 | {file.semester.name}
73 | {file.upvotes.length}
74 | {file.downvotes.length}
75 | {file.original_author.username}
76 |
77 |
78 | ))}
79 |
80 |
81 | ) : (
82 |
No results found.
83 | )}
84 |
85 |
86 | );
87 | };
88 |
89 | export default Results;
90 |
91 |
92 | function DownloadButton({ file }: { file: File }) {
93 | const [success, setSuccess] = useState(false);
94 | const handleDownload = () => {
95 | // Implement download functionality
96 | fetch("http://localhost:8080/request", {
97 | method: "POST",
98 | headers: {
99 | "Content-Type": "application/json",
100 | },
101 | body: JSON.stringify({ id: file.id, filename: file.filename, ip: file.original_author.ip_address }),
102 | })
103 | .then((response) => response.text())
104 | .then(() => {
105 | return fetch(
106 | `${import.meta.env.VITE_CENTRAL_SERVER}/api/files/${file.id}/add-peer/`,
107 | {
108 | method: "POST",
109 | headers: getAuthHeaders(),
110 | }
111 | )
112 | })
113 | .then((response) => {
114 | if (!response.ok) {
115 | throw new Error("Failed to add peer");
116 | }
117 | setSuccess(true);
118 | })
119 | .catch((error) => {
120 | console.error("Error:", error);
121 | setSuccess(false);
122 | });
123 | };
124 | return (
125 | <>
126 | {
127 | success === true ?
128 | Downloaded! :
129 | Download
130 | }
131 | >
132 | );
133 | }
--------------------------------------------------------------------------------
/frontend/src/screens/Signup.tsx:
--------------------------------------------------------------------------------
1 | import { Link, useNavigate } from "react-router-dom";
2 | import "../styles/Login.css"
3 | import { FormEvent } from "react";
4 | import { useSessionRedirect } from "../hooks/sessionRedirect";
5 | export default function Signup() {
6 | const navigate = useNavigate();
7 | useSessionRedirect();
8 |
9 | function handleSubmit(e: FormEvent) {
10 | e.preventDefault();
11 | const formData = new FormData(e.target as HTMLFormElement);
12 | const email = formData.get('email');
13 | const username = formData.get('username');
14 | const password = formData.get('password');
15 | console.log(import.meta.env.VITE_CENTRAL_SERVER)
16 | fetch(`${import.meta.env.VITE_CENTRAL_SERVER}/api/signup/`, {
17 | method: 'POST',
18 | headers: {
19 | 'Content-Type': 'application/json',
20 | },
21 | body: JSON.stringify({ email, username, password }),
22 | })
23 | .then(response => response.json())
24 | .then(() => {
25 | navigate('/login');
26 | })
27 | .catch((error) => {
28 | console.error('Error:', error);
29 | });
30 | }
31 | return (
32 |
33 |
50 |
51 | );
52 | }
--------------------------------------------------------------------------------
/frontend/src/styles/App.css:
--------------------------------------------------------------------------------
1 | html {
2 | width: 100%;
3 | }
4 | nav {
5 | display: flex;
6 | justify-content: space-between;
7 | align-items: center;
8 | padding: 1rem 2rem;
9 | margin: 0;
10 | width: 100%;
11 | background-color: #145DA0;
12 | border: none;
13 | border-radius: 0;
14 | position: fixed;
15 | top: 0;
16 | left: 0;
17 | z-index: 1000;
18 | box-sizing: border-box;
19 | }
20 | nav div {
21 | display: flex;
22 | align-items: center;
23 | gap: 1rem;
24 | }
25 | .App {
26 | display: flex;
27 | flex-direction: column;
28 | align-items: center;
29 | justify-content: center; /* If you're centering the content */
30 | height: 100vh; /* Full height */
31 | margin-top: 100px; /* Move up */
32 | width: 100%;
33 | }
34 |
35 | body {
36 | background-color: #0C2D48;
37 | font-family: 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif;
38 | width: 100%;
39 | }
40 |
--------------------------------------------------------------------------------
/frontend/src/styles/Login.css:
--------------------------------------------------------------------------------
1 | form {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: center;
5 | gap: 0.5rem;
6 | }
7 | main {
8 | display: flex;
9 | justify-content: center;
10 | align-items: center;
11 | height: 100vh;
12 | }
13 | h1 {
14 | text-align: center;
15 | }
16 |
17 | .form-input {
18 | display: flex;
19 | flex-direction: column;
20 | }
21 |
22 | .login-container {
23 | display: flex;
24 | flex-direction: column;
25 | align-items: center;
26 | justify-content: center;
27 | background: #B1D4E0;
28 | border-radius: 8px;
29 | box-shadow: 0 2px 4px rgba(0,0,0,0.1); /* shadow */;
30 | width: 40vh;
31 | }
32 |
33 | .submit-button {
34 | padding: 8px 16px;
35 | margin-top: 20px; /* Adds space above the button */
36 | background-color: #2E8BC0; /* Example background color */
37 | color: white; /* Text color */
38 | border: none;
39 | border-radius: 4px;
40 | cursor: pointer;
41 | font-family: 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif;
42 | }
--------------------------------------------------------------------------------
/frontend/src/styles/MainScreenWrapper.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | height: 100%;
3 | margin: 0;
4 | display: flex;
5 | justify-content: center;
6 | align-items: center;
7 | background: #0C2D48;
8 | font-family: 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif;
9 | }
10 |
11 | .filter-container {
12 | display: flex;
13 | flex-direction: column;
14 | align-items: center;
15 | justify-content: center;
16 | padding: 20px;
17 | background: #B1D4E0;
18 | gap: 15;
19 | border-radius: 8px;
20 | box-shadow: 0 2px 4px rgba(0,0,0,0.1); /* shadow */
21 | }
22 |
23 | .logo {
24 | height: 100px; /* Adjust this value as needed */
25 | width: auto; /* Keep the logo's aspect ratio */
26 | margin-bottom: 15px; /* Gap between logo and search box */
27 | }
28 |
29 | .filter-item + .filter-item {
30 | margin-top: 20px; /* space between filter items */
31 | }
32 |
33 | label {
34 | margin-bottom: 5px;
35 | font-weight: bold;
36 | }
37 |
38 | .filter-item > select {
39 | padding: 8px;
40 | border: 1px solid #2E8BC0;
41 | border-radius: 4px;
42 | width: 100%;
43 | }
44 |
45 | .filter-item {
46 | width: 100%;
47 | }
48 |
49 | .submit-button {
50 | padding: 8px 16px;
51 | margin-top: 20px; /* Adds space above the button */
52 | background-color: #2E8BC0; /* Example background color */
53 | color: white; /* Text color */
54 | border: none;
55 | border-radius: 4px;
56 | cursor: pointer;
57 | font-family: 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif;
58 | }
59 |
60 | .submit-button:hover {
61 | background-color: #1D6A96; /* Darker shade for hover effect */
62 | }
63 |
--------------------------------------------------------------------------------
/frontend/src/styles/RegisterFile.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | color: white;
3 | }
4 | .container {
5 | display: flex;
6 | gap: 5rem;
7 | justify-content: center;
8 | }
9 |
10 | .file-upload-card {
11 | padding: 1em;
12 | background-color: #B1D4E0;
13 | border-radius: 5px;
14 | }
15 | .file-upload-card h1 {
16 | color: black;
17 | }
18 |
19 | .file-card {
20 | padding: 1em;
21 | background-color: #2E8BC0;
22 | border-radius: 5px;
23 | display: flex;
24 | flex-direction: row;
25 | }
26 | .submit-button {
27 | padding: 8px 16px;
28 | margin-top: 20px; /* Adds space above the button */
29 | background-color: #2E8BC0; /* Example background color */
30 | color: white; /* Text color */
31 | border: none;
32 | border-radius: 4px;
33 | cursor: pointer;
34 | font-family: 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif;
35 | }
36 |
37 | th {
38 | text-align: center;
39 | padding: 1rem;
40 | border: 1px solid #FFFFFF;
41 | color: white;
42 | }
43 | td {
44 | text-align: center;
45 | border: 1px solid #FFFFFF;
46 | background-color: #B1D4E0;
47 | color: black;
48 | }
--------------------------------------------------------------------------------
/frontend/src/styles/Results.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | display: flex;
3 | flex-direction: column;
4 | height: 100vh;
5 | color: #FFFFFF;
6 | }
7 |
8 | .header {
9 | display: flex;
10 | justify-content: space-between;
11 | padding: 10px 20px;
12 | position: fixed;
13 | width: 100%;
14 | top: 0;
15 | left: 0;
16 | background-color: #145DA0;
17 | }
18 |
19 | .logo {
20 | height: 80px; /* Adjust according to preference */
21 | margin-right: auto;
22 | }
23 |
24 |
25 | .resultsContainer {
26 | display: flex;
27 | flex-direction: column;
28 | align-items: center;
29 | justify-content: center;
30 |
31 | margin: auto;
32 | width: 100vh;
33 | gap: 1rem;
34 | }
35 |
36 | .resultsTable {
37 | /* to be filled out */
38 | width: 100%;
39 | }
40 | .fileCard p {
41 | background-color: #1f4f7c;
42 | padding: 1rem;
43 | border-radius: 5px;
44 | }
45 | .fileCard {
46 | display: flex;
47 | flex-direction: row;
48 | align-items: center;
49 | justify-content: center;
50 | padding: 20px;
51 | gap: 1rem;
52 | background-color: #145DA0;
53 | border-radius: 8px;
54 | box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
55 | min-width: 100vh;
56 | }
57 |
58 | .searchAgainButton {
59 | height: 50px;
60 | padding: 4px 8px;
61 | background-color: #0C2D48;
62 | color: white;
63 | border: none;
64 | border-radius: 4px;
65 | font-family: 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif;
66 | cursor: pointer;
67 | margin-right: 35px;
68 | margin-top: 15px;
69 | }
70 |
71 | .searchAgainButton:hover {
72 | background-color: #B1D4E0; /* lighter shade for hover effect */
73 | }
74 |
75 | table {
76 | width: 100%;
77 | }
78 | th {
79 | text-align: center;
80 | padding: 1rem;
81 | border: 1px solid #FFFFFF;
82 | }
83 | td {
84 | text-align: center;
85 | border: 1px solid #FFFFFF;
86 | background-color: #B1D4E0;
87 | color: black;
88 | }
--------------------------------------------------------------------------------
/frontend/src/types/types.ts:
--------------------------------------------------------------------------------
1 | export interface Professor {
2 | id: number;
3 | name: string;
4 | }
5 | export interface Course {
6 | id: number;
7 | name: string;
8 | number: string;
9 | }
10 |
11 | export interface Topic {
12 | id: number;
13 | name: string;
14 | description: string;
15 | }
16 |
17 | export interface Semester {
18 | id: number;
19 | name: string;
20 | }
21 |
22 | export interface User {
23 | id: string;
24 | username: string;
25 | email: string;
26 | ip_address: string;
27 | points: number;
28 | }
29 |
30 | export interface File {
31 | id: string;
32 | filename: string;
33 | original_author: User;
34 | professor: Professor;
35 | course: Course;
36 | semester: Semester;
37 | upvotes: number[];
38 | downvotes: number[];
39 | }
40 |
41 | export enum Status {
42 | HOSTED = "HOSTED",
43 | PRIVATE = 'PRIVATE',
44 | }
45 | export interface RegisteredFile extends File {
46 | status: Status;
47 | }
--------------------------------------------------------------------------------
/frontend/src/utils/getAuthHeaders.ts:
--------------------------------------------------------------------------------
1 | import { getSessionCookie } from "../contexts/session";
2 | export const getAuthHeaders = () => {
3 | const token = getSessionCookie();
4 | console.log(token);
5 | return {
6 | 'Authorization': `Token ${token}`,
7 | 'Content-Type': 'application/json'
8 | };
9 | };
--------------------------------------------------------------------------------
/frontend/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "react-jsx",
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noFallthroughCasesInSwitch": true
22 | },
23 | "include": ["src"],
24 | "references": [{ "path": "./tsconfig.node.json" }]
25 | }
26 |
--------------------------------------------------------------------------------
/frontend/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true,
8 | "strict": true
9 | },
10 | "include": ["vite.config.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/frontend/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react-swc'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/test1.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import json
3 | import os
4 |
5 |
6 | def request_tests():
7 | url = "http://localhost:8080/request-tests"
8 | downloaded_files = []
9 | print("past url")
10 | for i in range(100):
11 | filename = f"TestFileTransfer.pdf"
12 |
13 | payload = json.dumps(
14 | {
15 | "id": 5,
16 | "filename": filename,
17 | "ip": "143.215.87.54",
18 | "new_filename": f"TestFileTransfer_{i}.pdf",
19 | }
20 | )
21 |
22 | headers = {
23 | "Content-Type": "application/json",
24 | }
25 |
26 | response = requests.get(url, headers=headers, data=payload)
27 | print("response received")
28 | print(payload)
29 | print(response)
30 |
31 | if response.status_code == 200:
32 | print(f"Request {i+1} successful")
33 | downloaded_files.append(f"bcdef_{i}.txt")
34 | else:
35 | print(f"Request {i+1} failed with status code {response.status_code}")
36 |
37 | if downloaded_files:
38 | file_paths = ", ".join(["./uploads/" + file for file in downloaded_files])
39 | return f"1000 files have been downloaded to {file_paths}", 200
40 | else:
41 | return "No files downloaded", 200
42 |
43 |
44 | request_tests()
45 |
--------------------------------------------------------------------------------