├── Procfile
├── .gitignore
├── images
├── Update.png
├── delete.png
└── projects.png
├── app.py
├── resume
├── templates
│ ├── img
│ │ ├── default.jpg
│ │ ├── envelope-o.svg
│ │ ├── phone.svg
│ │ ├── link.svg
│ │ ├── camera.svg
│ │ ├── video.svg
│ │ └── monitor.svg
│ ├── reset.html
│ ├── home.html
│ ├── skills.html
│ ├── reset_password.html
│ ├── id_post.html
│ ├── login.html
│ ├── account.html
│ ├── register.html
│ ├── acheive.html
│ ├── posts.html
│ ├── layout.html
│ ├── resume.html
│ ├── education.html
│ ├── projects.html
│ ├── experience.html
│ └── resume-template.html
├── static
│ ├── profiles
│ │ └── default.jpg
│ ├── main.css
│ ├── resume_template.css
│ └── resume.css
├── __init__.py
├── models.py
├── forms.py
└── routes.py
├── uwsgi.ini
├── requirements.txt
├── LICENSE
├── test.py
└── README.md
/Procfile:
--------------------------------------------------------------------------------
1 | web: uwsgi uwsgi.ini
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | resume/.env
3 | venv
4 | site.db
5 |
--------------------------------------------------------------------------------
/images/Update.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DiptoChakrabarty/Resume-Generator/HEAD/images/Update.png
--------------------------------------------------------------------------------
/images/delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DiptoChakrabarty/Resume-Generator/HEAD/images/delete.png
--------------------------------------------------------------------------------
/images/projects.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DiptoChakrabarty/Resume-Generator/HEAD/images/projects.png
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | from resume import app
2 |
3 | if __name__ == '__main__':
4 | app.run(host="0.0.0.0",debug=True)
5 |
--------------------------------------------------------------------------------
/resume/templates/img/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DiptoChakrabarty/Resume-Generator/HEAD/resume/templates/img/default.jpg
--------------------------------------------------------------------------------
/uwsgi.ini:
--------------------------------------------------------------------------------
1 | [uwsgi]
2 | http-socket = :$(PORT)
3 | master = true
4 | die-on-term = true
5 | module = app:app
6 | memory-report = true
--------------------------------------------------------------------------------
/resume/static/profiles/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DiptoChakrabarty/Resume-Generator/HEAD/resume/static/profiles/default.jpg
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | bcrypt==3.1.7
2 | blinker==1.4
3 | cffi==1.14.0
4 | click==7.1.2
5 | dnspython==1.16.0
6 | email-validator==1.1.1
7 | Flask==1.1.2
8 | Flask-Bcrypt==0.7.1
9 | Flask-Login==0.5.0
10 | Flask-Mail==0.9.1
11 | Flask-SQLAlchemy==2.4.3
12 | Flask-WTF==0.14.3
13 | idna==2.10
14 | itsdangerous==1.1.0
15 | Jinja2==2.11.2
16 | MarkupSafe==1.1.1
17 | pdfkit==0.6.1
18 | Pillow==7.2.0
19 | pycparser==2.20
20 | python-dotenv==0.14.0
21 | six==1.15.0
22 | SQLAlchemy==1.3.18
23 | Werkzeug==1.0.1
24 | WTForms==2.3.1
25 | uwsgi
--------------------------------------------------------------------------------
/resume/templates/img/envelope-o.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/resume/templates/img/phone.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/resume/templates/img/link.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/resume/__init__.py:
--------------------------------------------------------------------------------
1 | from flask import Flask
2 | from datetime import datetime
3 |
4 | from flask_sqlalchemy import SQLAlchemy
5 | from flask_bcrypt import Bcrypt
6 | from flask_login import LoginManager
7 | import subprocess as sp
8 | from flask_mail import Mail
9 |
10 | from dotenv import load_dotenv
11 | load_dotenv()
12 | import os
13 |
14 |
15 |
16 | app = Flask(__name__)
17 |
18 | app.config['SECRET_KEY'] = os.urandom(32).hex() #Prevents XSS
19 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
20 |
21 | app.config["MAIL_SERVER"] = "smtp.gmail.com"
22 | app.config["MAIL_PORT"] = 587
23 | app.config["MAIL_USE_TLS"] = True
24 | app.config["MAIL_USERNAME"] = os.environ["MAIL_USERNAME"]
25 | app.config["MAIL_PASSWORD"] = os.environ["PASSWORD"]
26 | mail = Mail(app)
27 |
28 | db = SQLAlchemy(app)
29 | db.create_all()
30 | bcrypt = Bcrypt(app)
31 | login_manager = LoginManager(app)
32 | login_manager.login_view = 'login' #provide login path first
33 |
34 | @app.before_first_request
35 | def create_tables():
36 | db.create_all()
37 |
38 |
39 |
40 |
41 | from resume import routes
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Dipto Chakrabarty
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/resume/templates/reset.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block content %}
3 |
32 |
33 | {% endblock content %}
--------------------------------------------------------------------------------
/resume/templates/img/camera.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
10 |
11 |
12 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/test.py:
--------------------------------------------------------------------------------
1 | from flask import Flask,request,jsonify
2 | from datetime import datetime
3 | import os
4 | from flask_sqlalchemy import SQLAlchemy
5 | from flask_bcrypt import Bcrypt
6 | from flask_login import LoginManager
7 | from pymongo import MongoClient
8 |
9 |
10 |
11 | app = Flask(__name__)
12 |
13 | app.config['SECRET_KEY'] = 'e23739c67eade607c64f90c3ebb479ca'
14 |
15 | '''app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
16 |
17 | db = SQLAlchemy(app)'''
18 |
19 | client= MongoClient("mongodb://localhost:27017")
20 | db = client.resume
21 | users = db["users"]
22 |
23 | bcrypt = Bcrypt(app)
24 |
25 |
26 |
27 | @app.route("/register",methods=['POST'])
28 | def register():
29 | data = request.get_json()
30 | username = data["username"]
31 | password = data["password"]
32 | email = data["email"]
33 | hashed = bcrypt.generate_password_hash(password).decode('utf-8')
34 | '''new_user = user(username=form.username.data,email=form.email.data,password=hashed)
35 | db.session.add(new_user)
36 | db.session.commit()'''
37 | users.insert({
38 | "username": username,
39 | "email": email,
40 | "password": hashed
41 | })
42 | ret={
43 | "status": 200,
44 | "msg": "user made"
45 | }
46 | #flash(f'Account Created for {fousername}!','success')
47 | return jsonify(ret)
48 |
49 |
50 |
51 | if __name__ == '__main__':
52 | app.run(debug=True)
--------------------------------------------------------------------------------
/resume/templates/img/video.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
9 |
14 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/resume/templates/home.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block content %}
3 |
4 | Welcome to Resume Generator
5 |
6 |
7 |
41 |
42 |
43 |
44 |
45 | {% endblock content %}
46 |
47 |
48 |
--------------------------------------------------------------------------------
/resume/templates/img/monitor.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
16 |
17 |
--------------------------------------------------------------------------------
/resume/templates/skills.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block content %}
3 |
4 |
5 |
42 |
43 | {% if skillsadded %}
44 |
45 |
Skills Added
46 | {% for skills in skillsadded %}
47 |
48 | {{ skills.skillname }}
49 |
50 | {% endfor %}
51 |
52 | {% endif %}
53 |
54 |
55 |
56 | {% endblock content %}
57 |
58 |
--------------------------------------------------------------------------------
/resume/templates/reset_password.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block content %}
3 |
48 |
49 | {% endblock content %}
--------------------------------------------------------------------------------
/resume/templates/id_post.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block content %}
3 |
4 |
5 |
6 |
10 |
{{ post.title }}
11 |
{{ post.content }}
12 | {% if post.author == current_user %}
13 |
17 | {% endif %}
18 |
19 |
20 |
21 |
39 | {% endblock content %}
40 |
41 |
--------------------------------------------------------------------------------
/resume/templates/login.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block content %}
3 |
53 |
54 |
55 |
56 | Dont Have An Account? Sign Up
57 |
58 |
59 | {% endblock content %}
--------------------------------------------------------------------------------
/resume/templates/account.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block content %}
3 |
63 |
64 |
65 |
66 | {% endblock content %}
67 |
68 |
--------------------------------------------------------------------------------
/resume/static/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: #fafafa;
3 | color: #333333;
4 | margin-top: 5rem;
5 | }
6 |
7 | h1, h2, h3, h4, h5, h6 {
8 | color: #444444;
9 | }
10 |
11 | .bg-steel {
12 | background-color: #5f788a;
13 | }
14 |
15 | .site-header .navbar-nav .nav-link {
16 | color: #cbd5db;
17 | }
18 |
19 | .site-header .navbar-nav .nav-link:hover {
20 | color: #ffffff;
21 | }
22 |
23 | .site-header .navbar-nav .nav-link.active {
24 | font-weight: 500;
25 | }
26 |
27 | .content-section {
28 | background: #ffffff;
29 | padding: 10px 20px;
30 | border: 1px solid #dddddd;
31 | border-radius: 3px;
32 | margin-bottom: 20px;
33 | }
34 |
35 | .article-title {
36 | color: #444444;
37 | }
38 |
39 | a.article-title:hover {
40 | color: #428bca;
41 | text-decoration: none;
42 | }
43 |
44 | .article-content {
45 | white-space: pre-line;
46 | }
47 |
48 | .article-img {
49 | height: 65px;
50 | width: 65px;
51 | margin-right: 16px;
52 | }
53 |
54 | .article-metadata {
55 | padding-bottom: 1px;
56 | margin-bottom: 4px;
57 | border-bottom: 1px solid #e3e3e3
58 | }
59 |
60 | .article-metadata a:hover {
61 | color: #333;
62 | text-decoration: none;
63 | }
64 |
65 | .article-svg {
66 | width: 25px;
67 | height: 25px;
68 | vertical-align: middle;
69 | }
70 |
71 | .account-img {
72 | height: 125px;
73 | width: 125px;
74 | margin-right: 20px;
75 | margin-bottom: 16px;
76 | }
77 |
78 | .account-heading {
79 | font-size: 2.5rem;
80 | }
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | .sidebar {
90 | margin: 0;
91 | padding: 0;
92 | width: 200px;
93 | background-color: #f1f1f1;
94 | position: fixed;
95 | height: 100%;
96 | overflow: auto;
97 | }
98 |
99 | .sidebar a {
100 | display: block;
101 | color: black;
102 | padding: 16px;
103 | text-decoration: none;
104 | }
105 |
106 | .sidebar a.active {
107 | background-color: #4CAF50;
108 | color: white;
109 | }
110 |
111 | .sidebar a:hover:not(.active) {
112 | background-color: #555;
113 | color: white;
114 | }
115 |
116 | div.content {
117 | margin-left: 200px;
118 | padding: 1px 16px;
119 | height: 1000px;
120 | }
121 |
122 | @media screen and (max-width: 700px) {
123 | .sidebar {
124 | width: 100%;
125 | height: auto;
126 | position: relative;
127 | }
128 | .sidebar a {float: left;}
129 | div.content {margin-left: 0;}
130 | }
131 |
132 | @media screen and (max-width: 400px) {
133 | .sidebar a {
134 | text-align: center;
135 | float: none;
136 | }
137 | }
--------------------------------------------------------------------------------
/resume/templates/register.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block content %}
3 |
73 |
74 |
75 |
76 |
77 |
78 | Already Have An Account? Sign In
79 |
80 |
81 | {% endblock content %}
--------------------------------------------------------------------------------
/resume/templates/acheive.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block content %}
3 |
4 |
5 |
57 |
58 | {% if achadded and title=="Acheivements" %}
59 |
60 |
Acheivements Added
61 | {% for acheive in achadded %}
62 |
63 | {{ acheive.achname }}
64 | {{ acheive.achdesc }}
65 |
66 |
67 |
71 |
72 |
90 |
91 | {% endfor %}
92 |
93 | {% endif %}
94 |
95 |
96 |
97 |
98 | {% endblock content %}
99 |
100 |
--------------------------------------------------------------------------------
/resume/templates/posts.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block content %}
3 |
4 |
5 |
99 |
100 |
101 |
102 |
103 | {% endblock content %}
--------------------------------------------------------------------------------
/resume/static/resume_template.css:
--------------------------------------------------------------------------------
1 | html {
2 | box-sizing: border-box;
3 | }
4 | *, *:before, *:after {
5 | box-sizing: inherit;
6 | }
7 |
8 | body {
9 | font-family: 'Source Sans Pro', sans-serif;
10 | line-height: 1.5;
11 | background: #F2F2F2;
12 | color: #323232;
13 | }
14 |
15 | img {
16 | max-width: 100%;
17 | }
18 |
19 | .icon {
20 | fill: currentColor;
21 | display: inline-block;
22 | font-size: inherit;
23 | height: 1em;
24 | overflow: visible;
25 | }
26 |
27 | a {
28 | color: #323232;
29 | text-decoration: none;
30 | }
31 |
32 | a:hover {
33 | text-decoration: underline;
34 | }
35 |
36 | .container {
37 | max-width: 960px;
38 | margin: 40px auto;
39 | padding: 32px;
40 | background: white;
41 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
42 | }
43 |
44 | section {
45 | display: grid;
46 | grid-template-columns: 1fr 4fr;
47 | grid-gap: 20px;
48 | padding: 24px 0;
49 | border-bottom: 1px solid lightgrey;
50 | }
51 |
52 | section:last-child {
53 | border-bottom: none;
54 | }
55 |
56 | .section-title {
57 | font-weight: bold;
58 | font-size: 18px;
59 | }
60 |
61 | /***********************************
62 | * =Main Info
63 | ***********************************/
64 |
65 | img.avatar {
66 | width: 130px;
67 | }
68 |
69 | .my-name {
70 | font-size: 48px;
71 | line-height: 1;
72 | }
73 |
74 | .my-title {
75 | font-size: 24px;
76 | font-weight: 300;
77 | color: #236FB2;
78 | }
79 |
80 | .links {
81 | display: flex;
82 | margin: 10px 0 20px 0;
83 | }
84 |
85 | .link-item {
86 | display: flex;
87 | align-items: center;
88 | flex: 1;
89 | }
90 |
91 | .link-item svg {
92 | margin-right: 6px;
93 | }
94 |
95 | /***********************************
96 | * =Experience
97 | ***********************************/
98 |
99 | .job {
100 | padding-bottom: 24px;
101 | margin-bottom: 24px;
102 | border-bottom: 1px solid lightgrey;
103 | }
104 |
105 | .job:last-child {
106 | border-bottom: none;
107 | }
108 |
109 | .job-title-container {
110 | display: flex;
111 | justify-content: space-between;
112 | margin-bottom: 20px;
113 | font-size: 18px;
114 | }
115 |
116 | .job-company {
117 | font-weight: bold;
118 | line-height: 1.2;
119 | }
120 |
121 | /***********************************
122 | * =Skills
123 | ***********************************/
124 |
125 | .skills-container {
126 | display: grid;
127 | grid-template-columns: 1fr 1fr 1fr;
128 | grid-gap: 30px;
129 | margin-bottom: 24px;
130 | }
131 |
132 | .skills-container ul {
133 | margin-left: 20px;
134 | list-style-type: disc;
135 | }
136 |
137 | /***********************************
138 | * =Interests
139 | ***********************************/
140 |
141 | .interests-container {
142 | display: flex;
143 | justify-content: space-between;
144 | }
145 |
146 | .interests-container img {
147 | height: 35px;
148 | opacity: 0.75;
149 | }
150 |
151 | /***********************************
152 | * =References
153 | ***********************************/
154 |
155 | .reference {
156 | font-size: 18px;
157 | }
158 |
159 | .reference-details {
160 | margin-bottom: 20px;
161 | }
162 |
163 | @media only screen and (max-width : 768px) {
164 | section {
165 | grid-template-columns: 1fr;
166 | }
167 |
168 | .links, .job-title-container {
169 | flex-direction: column;
170 | }
171 |
172 | .skills-container {
173 | grid-template-columns: 1fr 1fr;
174 | }
175 |
176 | .interests-container {
177 | flex-wrap: wrap;
178 | justify-content: flex-start;
179 | }
180 |
181 | .interests-container img {
182 | margin-right: 32px;
183 | margin-bottom: 16px;
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/resume/templates/layout.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | {% if title %}
15 | {{ title }}
16 | {% else %}
17 | Home Page
18 | {% endif %}
19 |
20 |
21 |
48 |
49 |
50 |
51 | {% with messages = get_flashed_messages(with_categories=true) %}
52 | {% if messages %}
53 | {% for category,message in messages %}
54 |
55 | {{ message }}
56 |
57 | {% endfor %}
58 | {% endif %}
59 | {% endwith %}
60 | {% block content %}{% endblock %}
61 |
62 |
63 | {%if title == "New Resume" or title=="Projects" or title=="Education" or title=="Experience" or title == "Skills" or title == "Acheivements" %}
64 |
78 |
79 | {% endif %}
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## Table of Contents
3 |
4 | * [About the Project](#about-the-project)
5 | * [Built With](#built-with)
6 | * [Installation](#installation)
7 | * [Contributing](#contributing)
8 | * [License](#license)
9 | * [Contact](#contact)
10 | * [Acknowledgements](#acknowledgements)
11 |
12 | ### Project at View
13 |
14 | * [Project Video Demo](https://www.youtube.com/watch?v=M-N31tPJa0I&t=15s)
15 |
16 | * Add Your Details
17 |
18 |
19 | * Update your Details
20 |
21 |
22 | * Delete Details
23 |
24 |
25 | and much more
26 |
27 |
28 |
29 | ## About The Project
30 |
31 | - Build your Resume with this application , register and login to create your own resume easily.
32 | - Run the program and go to localhost:5000
33 | - Add user details and entries
34 | - Users can add fields
35 | - Users can also update or delete entries
36 |
37 | ### Built With
38 | This section should list any major frameworks that you built your project using. Leave any add-ons/plugins for the acknowledgements section. Here are a few examples.
39 | * Flask
40 | * sqlalchemy
41 | * Python
42 |
43 | ### Installation
44 |
45 |
46 | 1. Clone the repo
47 | ```sh
48 | git clone https://github.com/DiptoChakrabarty/Resume-Generator
49 | ```
50 | 2. Enter Directory
51 | ```sh
52 | cd Resume-Generator
53 | ```
54 | 3. Install all packages
55 | ```sh
56 | pip install -r requirements.txt
57 | ```
58 | 4. Remove site.db file to start fresh database
59 | ```sh
60 | rm resume/site.db
61 | ```
62 | 5. Setup Env Variables
63 |
64 | create file .env inside folder resume
65 |
66 | Add the following
67 | ```sh
68 | MAIL_USERNAME="{{ your gmail username }}"
69 | PASSWORD="{{ your password }}"
70 | ```
71 |
72 | 6. Run webserver
73 | ```sh
74 | python3 app.py
75 | ```
76 |
77 | # Contributing
78 |
79 | When contributing to this repository, please first discuss the change you wish to make via issue,
80 | email, or any other method with the owners of this repository before making a change.
81 |
82 |
83 | ## Pull Request Process
84 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a
85 | build.
86 | 2. Update the README.md with details of changes to the interface, this includes new environment
87 | variables, exposed ports, useful file locations and container parameters.
88 | 3. Only send your pull requests to the development branch where once we reach a stable point
89 | it will be merged with the master branch
90 | 4. Associate each Pull Request with the required issue number
91 |
92 | ## Branch Policy
93 | * development: If you are making a contribution make sure to send your Pull Request to this branch . All
94 | developments goes in this branch.
95 | * master: After significant features/bug-fixes are accumulated in development branch we merge it with the master branch.
96 |
97 | ## Contribution Practices
98 | * Write clear and meaningful commit messages.
99 | * If you report a bug please provide steps to reproduce the bug.
100 | * In case of changing the backend routes please submit an updated routes documentation for the same.
101 | * If there is an UI related change it would be great if you could attach a screenshot
102 | with the resultant changes so it is easier to review for the maintainers
103 |
104 | ## License
105 |
106 | Distributed under the MIT License. See `LICENSE` for more information.
107 |
108 |
109 |
110 |
111 | ## Contact
112 |
113 | - My Name - Dipto Chakrabarty
114 |
115 | - You can contact me at diptochuck123@gmail.com
116 |
117 | - Project Link: [https://github.com/DiptoChakrabarty/website](https://github.com/DiptoChakrabarty/website)
118 |
119 |
120 |
121 | ## Acknowledgements
122 | * [GitHub Emoji Cheat Sheet](https://www.webpagefx.com/tools/emoji-cheat-sheet)
123 | * [Img Shields](https://shields.io)
124 | * [Choose an Open Source License](https://choosealicense.com)
125 | * [GitHub Pages](https://pages.github.com)
126 | * [Animate.css](https://daneden.github.io/animate.css)
127 | * [Loaders.css](https://connoratherton.com/loaders)
128 | * [Slick Carousel](https://kenwheeler.github.io/slick)
129 | * [Smooth Scroll](https://github.com/cferdinandi/smooth-scroll)
130 | * [Sticky Kit](http://leafo.net/sticky-kit)
131 | * [JVectorMap](http://jvectormap.com)
132 | * [Font Awesome](https://fontawesome.com)
133 |
--------------------------------------------------------------------------------
/resume/templates/resume.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ current_user.username }} Resume
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
{{ usr.name }}
28 | {{ usr.designation }}
29 |
30 |
31 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
Personal Profile
46 |
47 |
48 |
49 |
{{ usr.profile }}
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
Education
58 |
59 |
60 |
61 |
62 | {% for education in edu %}
63 |
64 | {{ education.name }}
65 | Student
66 | {{ education.start }} - {{ education.end }}
67 | CGPA - {{ education.cgpa }}
68 |
69 |
70 | {% endfor %}
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
Work Experience
80 |
81 |
82 |
83 | {% for experience in exp %}
84 |
85 | {{ experience.company }}
86 | {{ experience.startexp }} - {{ experience.endexp }}
87 | {{ experience.content }}
88 |
89 | {% endfor %}
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
Key Skills
101 |
102 |
103 |
104 |
105 | {% for skills in sk %}
106 | {{ skills.skillname }}
107 | {% endfor %}
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
Projects
116 |
117 |
118 |
119 | {% for project in pro %}
120 |
121 | {{ project.projectname }}
122 | {{ project.startpro }} - {{ project.endpro }}
123 | {{ project.description }}
124 | {{ project.url }}
125 |
126 |
127 | {% endfor %}
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
Acheivements
137 |
138 |
139 |
140 | {% for acheive in achmade %}
141 |
142 | {{ acheive.achname }}
143 | {{ acheive.achdesc }}
144 |
145 |
146 |
147 | {% endfor %}
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
163 |
168 |
169 |
--------------------------------------------------------------------------------
/resume/templates/education.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block content %}
3 |
4 |
5 |
80 |
81 |
82 | {% if eduadded and title=="Education"%}
83 |
84 |
Education Added
85 |
86 | {% for education in eduadded %}
87 |
88 | {{ education.name }}
89 | Student
90 | {{ education.start }} - {{ education.end }}
91 | CGPA - {{ education.cgpa }}
92 |
93 |
97 |
98 |
99 |
100 |
101 |
107 |
113 |
114 |
115 |
116 |
117 | {% endfor %}
118 |
119 |
120 |
121 |
122 | {% endif %}
123 |
124 |
125 |
126 |
127 | {% endblock content %}
--------------------------------------------------------------------------------
/resume/templates/projects.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block content %}
3 |
4 |
5 |
99 |
100 | {% if proadded and title == "Projects" %}
101 |
102 |
Projects Added
103 |
104 | {% for project in proadded %}
105 |
106 | {{ project.projectname }}
107 | {{ project.startpro }} - {{ project.endpro }}
108 | {{ project.description }}
109 | {{ project.url }}
110 |
111 |
112 |
Update
113 |
Delete
114 |
115 |
116 |
117 |
118 |
119 |
125 |
131 |
132 |
133 |
134 |
135 | {% endfor %}
136 |
137 |
138 |
139 | {% endif %}
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 | {% endblock content %}
--------------------------------------------------------------------------------
/resume/models.py:
--------------------------------------------------------------------------------
1 | from resume import db,login_manager,app
2 | from flask_login import UserMixin
3 | from datetime import datetime
4 | from itsdangerous import TimedJSONWebSignatureSerializer as serializer
5 |
6 |
7 | @login_manager.user_loader
8 | def load_user(id):
9 | return UserModel.query.get(int(id))
10 |
11 |
12 | class UserModel(db.Model,UserMixin):
13 | __tablename__= "user"
14 | id = db.Column(db.Integer, primary_key=True)
15 | username = db.Column(db.String(20),unique=True,nullable=False)
16 | email = db.Column(db.String(120),unique=True,nullable=False)
17 | image_file = db.Column(db.String(20),nullable=False,default='default.jpg')
18 | password = db.Column(db.String(60),nullable=False)
19 | #education
20 | education = db.relationship('education',backref='edu',lazy=True)
21 | #experience
22 | experience = db.relationship('experience',backref='exp',lazy=True)
23 | #projects
24 | projects = db.relationship('projects',backref='pro',lazy=True)
25 | #userdetails
26 | userdetails = db.relationship('userdetails',backref='details',lazy=True)
27 | #skills
28 | skills = db.relationship('skills',backref='skill',lazy=True)
29 | #achievements
30 | achievements = db.relationship('achievements',backref='ach',lazy=True)
31 |
32 | def __retr__(self):
33 | return "User {} Email {} Image {}".format(self.username,self.email,self.image_file)
34 |
35 |
36 | def add_to_database(self):
37 | db.session.add(self)
38 | db.session.commit()
39 |
40 | def delete_from_database(self):
41 | db.session.delete(self)
42 | db.session.commit()
43 |
44 | @classmethod
45 | def find_by_username(cls,username):
46 | return cls.query.filter_by(username=username).first()
47 |
48 | @classmethod
49 | def find_by_email(cls,email):
50 | return cls.query.filter_by(email=email).first()
51 |
52 | def reset_token(self,expires_sec=1800):
53 | s = serializer(app.config['SECRET_KEY'],expires_sec)
54 | return s.dumps({"user_id": self.id}).decode("utf-8")
55 |
56 | @staticmethod
57 | def verify_token(token):
58 | s = serializer(app.config['SECRET_KEY'])
59 | try:
60 | user_id = s.loads(token)['user_id']
61 | except:
62 | return None
63 | return UserModel.query.get(user_id)
64 |
65 |
66 |
67 |
68 | class userdetails(db.Model):
69 | id = db.Column(db.Integer, primary_key=True)
70 | name = db.Column(db.String(20),nullable=False)
71 | email = db.Column(db.String(120),nullable=False)
72 | designation = db.Column(db.String(120),nullable=False)
73 | phoneno = db.Column(db.String(15),nullable=False)
74 | profile = db.Column(db.Text,nullable=False)
75 | user_id= db.Column(db.Integer,db.ForeignKey('user.id'),nullable=False)
76 |
77 |
78 |
79 |
80 |
81 |
82 | class education(db.Model):
83 | id = db.Column(db.Integer, primary_key=True)
84 | name = db.Column(db.String(100),nullable=False)
85 | start = db.Column(db.DateTime,nullable=False,default=datetime.today())
86 | end = db.Column(db.DateTime,nullable=False,default=datetime.today())
87 | cgpa = db.Column(db.Integer,nullable=False)
88 | user_id= db.Column(db.Integer,db.ForeignKey('user.id'),nullable=False)
89 |
90 | def __retr__(self):
91 | return "name {} cgpa {} user_id {}".format(self.name,self.cgpa,self.user_id)
92 |
93 |
94 |
95 |
96 |
97 | class experience(db.Model):
98 | id = db.Column(db.Integer, primary_key=True)
99 | company = db.Column(db.String(100),nullable=False)
100 | position = db.Column(db.String(100),nullable=False)
101 | startexp = db.Column(db.DateTime,nullable=False,default=datetime.today())
102 | endexp = db.Column(db.DateTime,nullable=False,default=datetime.today())
103 | content = db.Column(db.Text,nullable=False)
104 | user_id= db.Column(db.Integer,db.ForeignKey('user.id'),nullable=False)
105 |
106 | def __retr__(self):
107 | return "company {} position {} user_id {}".format(self.company,self.position,self.user_id)
108 |
109 |
110 |
111 | class projects(db.Model):
112 | id = db.Column(db.Integer, primary_key=True)
113 | projectname = db.Column(db.String(100),nullable=False)
114 | startpro = db.Column(db.DateTime,nullable=False,default=datetime.today())
115 | endpro = db.Column(db.DateTime,nullable=False,default=datetime.today())
116 | description = db.Column(db.Text,default=None)
117 | url = db.Column(db.String(100),default=None)
118 | user_id= db.Column(db.Integer,db.ForeignKey('user.id'),nullable=False)
119 |
120 | def __retr__(self):
121 | return "projectname {} user_id {}".format(self.projectname,self.user_id)
122 |
123 | class skills(db.Model):
124 | id = db.Column(db.Integer, primary_key=True)
125 | skillname = db.Column(db.String(100),nullable=False)
126 | user_id= db.Column(db.Integer,db.ForeignKey('user.id'),nullable=False)
127 |
128 | class achievements(db.Model):
129 | id = db.Column(db.Integer, primary_key=True)
130 | achname = db.Column(db.String(100),nullable=False)
131 | achdesc = db.Column(db.Text(100),nullable=False)
132 | user_id= db.Column(db.Integer,db.ForeignKey('user.id'),nullable=False)
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 | def init_db():
149 | db.create_all()
150 |
151 |
152 | if __name__ == '__main__':
153 | init_db()
154 |
--------------------------------------------------------------------------------
/resume/templates/experience.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block content %}
3 |
4 |
5 |
6 |
97 |
98 |
99 |
100 |
101 |
102 |
103 | {% if expadded and title== "Experience" %}
104 |
105 |
Experience Added
106 |
107 | {% for experience in expadded %}
108 |
109 | {{ experience.company }}
110 | {{ experience.startexp }} - Present
111 | {{ experience.content }}
112 |
113 |
114 |
Update
115 |
Delete
116 |
117 |
118 |
119 |
120 |
121 |
127 |
133 |
134 |
135 |
136 | {% endfor %}
137 |
138 |
139 |
140 |
141 | {% endif %}
142 |
143 |
144 |
145 | {% endblock content %}
--------------------------------------------------------------------------------
/resume/forms.py:
--------------------------------------------------------------------------------
1 | from flask_wtf import FlaskForm
2 | from flask_wtf.file import FileAllowed,FileField
3 | from wtforms import StringField, PasswordField,SubmitField,BooleanField,TextAreaField
4 | from wtforms.fields.html5 import DateField
5 | from wtforms.validators import DataRequired,Length,Email,EqualTo,ValidationError,Regexp
6 | from resume.models import UserModel
7 | from flask_login import current_user
8 |
9 | class RegistrationForm(FlaskForm):
10 | username = StringField('Username',
11 | validators=[DataRequired(),Length(min=5,max=20)])
12 | email = StringField('Email',
13 | validators=[DataRequired(),Email()])
14 | password = PasswordField('Password',
15 | validators=[DataRequired()])
16 | confirm_password = PasswordField('Confirm Password',
17 | validators=[DataRequired(),EqualTo('password')])
18 | submit = SubmitField('Sign Up')
19 |
20 | def validate_username(self,username):
21 | users = UserModel.find_by_username(username.data)
22 | if users:
23 | raise ValidationError('Username used already')
24 |
25 | def validate_email(self,email):
26 | email = UserModel.find_by_email(email.data)
27 | if email:
28 | raise ValidationError('Email used already')
29 |
30 |
31 | class LoginForm(FlaskForm):
32 | email = StringField('Email',
33 | validators=[DataRequired(),Email()])
34 | password = PasswordField('Password',
35 | validators=[DataRequired()])
36 | remember = BooleanField('Remember Me')
37 | submit = SubmitField('Login')
38 |
39 |
40 | class account(FlaskForm):
41 | new_username = StringField("New Username",
42 | validators=[DataRequired(),Length(min=3,max=20)])
43 | new_email = StringField("New EmailId",
44 | validators=[Email(),DataRequired()])
45 | '''new_password = PasswordField("New Password",
46 | validators=[DataRequired(),Length(min=7,max=15)])
47 | confirm_new_password = PasswordField("Confirm New Password",
48 | validators=[DataRequired(),Length(min=7,max=15)]) '''
49 | picture = FileField("Update Profile Picture",validators=[FileAllowed(['jpg','png'])])
50 | submit = SubmitField('Update Account')
51 | def validate_username(self,new_username):
52 | if new_username.data != current_user.username:
53 | username = UserModel.find_by_username(new_username.data)
54 | if username:
55 | raise ValidationError("Username Already present")
56 | def validate_email(self,new_email):
57 | if new_email != current_user.email:
58 | emailid = UserModel.find_by_email(new_email.data)
59 | if emailid:
60 | return ValidationError("Email Id used already")
61 |
62 |
63 | class PostingForm(FlaskForm):
64 | title = StringField("Name",
65 | validators=[DataRequired(),Length(min=5)])
66 | content = TextAreaField("Description",
67 | validators=[DataRequired(),Length(min=15)])
68 | submit = SubmitField("Create Post")
69 |
70 |
71 |
72 | #Education
73 | class useredu(FlaskForm):
74 | college = StringField("College",
75 | validators=[DataRequired(),Length(min=5)])
76 | start = DateField('Start Date', format='%Y-%m-%d',
77 | validators=[DataRequired()])
78 | end = DateField('End Date', format='%Y-%m-%d',
79 | validators=[DataRequired()])
80 |
81 | def validate_end(form, field):
82 | if field.data < form.start.data:
83 | raise ValidationError("End date cannot be earlier than start date.")
84 |
85 | cgpa = StringField("CGPA",
86 | validators=[DataRequired()])
87 | submit = SubmitField("Add Education")
88 |
89 |
90 | #Work Experience
91 | class userexp(FlaskForm):
92 | company = StringField("Company",
93 | validators=[DataRequired(),Length(min=5)])
94 | position = StringField("Position",
95 | validators=[DataRequired(),Length(min=5)])
96 | startexp = DateField('Start Date', format='%Y-%m-%d',
97 | validators=[DataRequired()])
98 | endexp = DateField('End Date', format='%Y-%m-%d',
99 | validators=[DataRequired()])
100 |
101 | def validate_endexp(form, field):
102 | if field.data < form.startexp.data:
103 | raise ValidationError("End date cannot be earlier than start date.")
104 |
105 | content = TextAreaField("Description",
106 | validators=[DataRequired(),Length(min=15)])
107 | submit = SubmitField("Add Work Experience")
108 |
109 | # Projects
110 | class userpro(FlaskForm):
111 | projectname = StringField("Project Name",
112 | validators=[DataRequired(),Length(min=3)])
113 | startpro = DateField('Start Date', format='%Y-%m-%d',
114 | validators=[DataRequired()])
115 | endpro = DateField('End Date', format='%Y-%m-%d',
116 | validators=[DataRequired()])
117 |
118 | def validate_endpro(form, field):
119 | if field.data < form.startpro.data:
120 | raise ValidationError("End date cannot be earlier than start date.")
121 |
122 | description = TextAreaField("Description",
123 | validators=[Length(min=10)])
124 | url = StringField("Project Url",
125 | validators=[Length(min=5)])
126 | submit = SubmitField("Add Projects")
127 |
128 |
129 | class resumebuilder(FlaskForm):
130 | name= StringField("Name",
131 | validators=[DataRequired(),Length(min=5)])
132 | designation = StringField("Designation",
133 | validators=[DataRequired(),Length(min=3)])
134 | email = StringField("Email Id",
135 | validators=[Email(),DataRequired()])
136 | phoneno= StringField("Phone No",
137 | validators=[Regexp('^[0-9]*$'),DataRequired()])
138 | profile = TextAreaField("Description",
139 | validators=[Length(min=10)])
140 |
141 |
142 | submit = SubmitField("Create Resume")
143 |
144 |
145 | class usersk(FlaskForm):
146 | skillname = StringField("Skill Name",
147 | validators=[DataRequired(),Length(min=3)])
148 |
149 | submit = SubmitField("Add Another skill")
150 |
151 | class achieve(FlaskForm):
152 | achname = StringField("Acheivement",
153 | validators=[DataRequired(),Length(min=3)])
154 | achdesc = TextAreaField("Description",
155 | validators=[Length(min=10)])
156 |
157 |
158 | submit = SubmitField("Add")
159 |
160 | class requestresetform(FlaskForm):
161 | email = StringField("Email",
162 | validators=[DataRequired(),Length(min=5)])
163 | submit = SubmitField("Reset Password")
164 |
165 | def validate_email(self,email):
166 | user_email = UserModel.find_by_email(email.data)
167 | if user_email is None:
168 | raise ValidationError("The email is unverified please register using this email")
169 |
170 | class resetpassword(FlaskForm):
171 | password = PasswordField('Password',
172 | validators=[DataRequired()])
173 | confirm_password = PasswordField('Confirm Password',
174 | validators=[DataRequired(),EqualTo('password')])
175 | submit = SubmitField('Change Password')
176 |
177 |
178 |
179 |
180 |
181 |
--------------------------------------------------------------------------------
/resume/static/resume.css:
--------------------------------------------------------------------------------
1 | html,body,div,span,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,abbr,address,cite,code,del,dfn,em,img,ins,kbd,q,samp,small,strong,sub,sup,var,b,i,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,figcaption,figure,footer,header,hgroup,menu,nav,section,summary,time,mark,audio,video {
2 | border:0;
3 | font:inherit;
4 | font-size:100%;
5 | margin:0;
6 | padding:0;
7 | vertical-align:baseline;
8 | height: auto;
9 | }
10 |
11 | article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section {
12 | display:block;
13 | }
14 |
15 | html, body {background: #181818; font-family: 'Lato', helvetica, arial, sans-serif; font-size: 16px; color: #222;}
16 |
17 | .clear {clear: both;}
18 |
19 | p {
20 | font-size: 1em;
21 | line-height: 1.4em;
22 | margin-bottom: 20px;
23 | color: #444;
24 | }
25 |
26 | #cv {
27 | width: 100%;
28 | max-width: 800px;
29 | background: #f3f3f3;
30 | margin: 30px auto;
31 | height: auto;
32 | }
33 |
34 | .mainDetails {
35 | padding: 25px 35px;
36 | border-bottom: 2px solid #0318fc;
37 | background: #ededed;
38 | }
39 |
40 | #name h1 {
41 | font-size: 2.5em;
42 | font-weight: 700;
43 | font-family: 'Rokkitt', Helvetica, Arial, sans-serif;
44 | margin-bottom: -6px;
45 | }
46 |
47 | #name h2 {
48 | font-size: 2em;
49 | margin-left: 2px;
50 | font-family: 'Rokkitt', Helvetica, Arial, sans-serif;
51 | }
52 |
53 | #mainArea {
54 | padding: 0 40px;
55 | }
56 |
57 | #headshot {
58 | width: 12.5%;
59 | float: left;
60 | margin-right: 30px;
61 | }
62 |
63 | #headshot img {
64 | width: 100%;
65 | height: auto;
66 | -webkit-border-radius: 50px;
67 | border-radius: 50px;
68 | }
69 |
70 | #name {
71 | float: left;
72 | }
73 |
74 | #contactDetails {
75 | float: right;
76 | }
77 |
78 | #contactDetails ul {
79 | list-style-type: none;
80 | font-size: 0.9em;
81 | margin-top: 2px;
82 | }
83 |
84 | #contactDetails ul li {
85 | margin-bottom: 3px;
86 | color: #444;
87 | }
88 |
89 | #contactDetails ul li a, a[href^=tel] {
90 | color: #444;
91 | text-decoration: none;
92 | -webkit-transition: all .3s ease-in;
93 | -moz-transition: all .3s ease-in;
94 | -o-transition: all .3s ease-in;
95 | -ms-transition: all .3s ease-in;
96 | transition: all .3s ease-in;
97 | }
98 |
99 | #contactDetails ul li a:hover {
100 | color: #0318fc;
101 | }
102 |
103 |
104 | section {
105 | border-top: 1px solid #dedede;
106 | padding: 20px 0 0;
107 | }
108 |
109 | section:first-child {
110 | border-top: 0;
111 | }
112 |
113 | section:last-child {
114 | padding: 20px 0 10px;
115 | }
116 |
117 | .sectionTitle {
118 | float: left;
119 | width: 25%;
120 | }
121 |
122 | .sectionContent {
123 | float: right;
124 | width: 72.5%;
125 | }
126 |
127 | .sectionTitle h1 {
128 | font-family: 'Rokkitt', Helvetica, Arial, sans-serif;
129 | font-style: italic;
130 | font-size: 1.5em;
131 | color: #0318fc;
132 | }
133 |
134 | .sectionContent h2 {
135 | font-family: 'Rokkitt', Helvetica, Arial, sans-serif;
136 | font-size: 1.5em;
137 | margin-bottom: -2px;
138 | }
139 |
140 | .subDetails {
141 | font-size: 0.8em;
142 | font-style: italic;
143 | margin-bottom: 3px;
144 | }
145 |
146 | .keySkills {
147 | list-style-type: none;
148 | -moz-column-count:3;
149 | -webkit-column-count:3;
150 | column-count:3;
151 | margin-bottom: 20px;
152 | font-size: 1em;
153 | color: #444;
154 | }
155 |
156 | .keySkills ul li {
157 | margin-bottom: 3px;
158 | }
159 |
160 | @media all and (min-width: 602px) and (max-width: 800px) {
161 | #headshot {
162 | display: none;
163 | }
164 |
165 | .keySkills {
166 | -moz-column-count:2;
167 | -webkit-column-count:2;
168 | column-count:2;
169 | }
170 | }
171 |
172 | @media all and (max-width: 601px) {
173 | #cv {
174 | width: 95%;
175 | margin: 10px auto;
176 | min-width: 280px;
177 | }
178 |
179 | #headshot {
180 | display: none;
181 | }
182 |
183 | #name, #contactDetails {
184 | float: none;
185 | width: 100%;
186 | text-align: center;
187 | }
188 |
189 | .sectionTitle, .sectionContent {
190 | float: none;
191 | width: 100%;
192 | }
193 |
194 | .sectionTitle {
195 | margin-left: -2px;
196 | font-size: 1.25em;
197 | }
198 |
199 | .keySkills {
200 | -moz-column-count:2;
201 | -webkit-column-count:2;
202 | column-count:2;
203 | }
204 | }
205 |
206 | @media all and (max-width: 480px) {
207 | .mainDetails {
208 | padding: 15px 15px;
209 | }
210 |
211 | section {
212 | padding: 15px 0 0;
213 | }
214 |
215 | #mainArea {
216 | padding: 0 25px;
217 | }
218 |
219 |
220 | .keySkills {
221 | -moz-column-count:1;
222 | -webkit-column-count:1;
223 | column-count:1;
224 | }
225 |
226 | #name h1 {
227 | line-height: .8em;
228 | margin-bottom: 4px;
229 | }
230 | }
231 |
232 | @media print {
233 | #cv {
234 | width: 100%;
235 | }
236 | }
237 |
238 | @-webkit-keyframes reset {
239 | 0% {
240 | opacity: 0;
241 | }
242 | 100% {
243 | opacity: 0;
244 | }
245 | }
246 |
247 | @-webkit-keyframes fade-in {
248 | 0% {
249 | opacity: 0;
250 | }
251 | 40% {
252 | opacity: 0;
253 | }
254 | 100% {
255 | opacity: 1;
256 | }
257 | }
258 |
259 | @-moz-keyframes reset {
260 | 0% {
261 | opacity: 0;
262 | }
263 | 100% {
264 | opacity: 0;
265 | }
266 | }
267 |
268 | @-moz-keyframes fade-in {
269 | 0% {
270 | opacity: 0;
271 | }
272 | 40% {
273 | opacity: 0;
274 | }
275 | 100% {
276 | opacity: 1;
277 | }
278 | }
279 |
280 | @keyframes reset {
281 | 0% {
282 | opacity: 0;
283 | }
284 | 100% {
285 | opacity: 0;
286 | }
287 | }
288 |
289 | @keyframes fade-in {
290 | 0% {
291 | opacity: 0;
292 | }
293 | 40% {
294 | opacity: 0;
295 | }
296 | 100% {
297 | opacity: 1;
298 | }
299 | }
300 |
301 | .instaFade {
302 | -webkit-animation-name: reset, fade-in;
303 | -webkit-animation-duration: 0.5s;
304 | -webkit-animation-timing-function: ease-in;
305 |
306 | -moz-animation-name: reset, fade-in;
307 | -moz-animation-duration: 0.5s;
308 | -moz-animation-timing-function: ease-in;
309 |
310 | animation-name: reset, fade-in;
311 | animation-duration: 0.5s;
312 | animation-timing-function: ease-in;
313 | }
314 |
315 | .quickFade {
316 | -webkit-animation-name: reset, fade-in;
317 | -webkit-animation-duration: 0.5s;
318 | -webkit-animation-timing-function: ease-in;
319 |
320 | -moz-animation-name: reset, fade-in;
321 | -moz-animation-duration: 0.5s;
322 | -moz-animation-timing-function: ease-in;
323 |
324 | animation-name: reset, fade-in;
325 | animation-duration: 0.5s;
326 | animation-timing-function: ease-in;
327 | }
328 |
329 | .delayOne {
330 | -webkit-animation-delay: 0, .5s;
331 | -moz-animation-delay: 0, .5s;
332 | animation-delay: 0, .5s;
333 | }
334 |
335 | .delayTwo {
336 | -webkit-animation-delay: 0, 1s;
337 | -moz-animation-delay: 0, 1s;
338 | animation-delay: 0, 1s;
339 | }
340 |
341 | .delayThree {
342 | -webkit-animation-delay: 0, 0.5s;
343 | -moz-animation-delay: 0, 0.5s;
344 | animation-delay: 0, 0.5s;
345 | }
346 |
347 | .delayFour {
348 | -webkit-animation-delay: 0, 2s;
349 | -moz-animation-delay: 0, 2s;
350 | animation-delay: 0, 2s;
351 | }
352 |
353 | .delayFive {
354 | -webkit-animation-delay: 0, 0.5s;
355 | -moz-animation-delay: 0, 0.5s;
356 | animation-delay: 0, 0.5s;
357 | }
--------------------------------------------------------------------------------
/resume/templates/resume-template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Resume Template
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
Your Name
18 |
Job Title
19 |
33 |
34 |
Netlify Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit maiores explicabo nam similique quaerat, porro perferendis adipisci molestias eius dolore eaque, consequatur placeat voluptates consequuntur! Quis quia incidunt aut, repellat aspernatur placeat praesentium vel enim eaque, aperiam, in corrupti tenetur.
35 |
36 |
37 |
38 |
39 | Experience
40 |
41 |
42 |
43 |
44 |
Company Name
45 |
Job Title
46 |
47 |
48 | Jan 2013 - Jan 2014
49 |
50 |
51 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Illum voluptate iure quasi inventore doloremque exercitationem maiores libero corrupti magnam praesentium voluptatem facere optio recusandae sit debitis tenetur beatae, voluptatibus itaque magni, est. Fugiat nostrum nemo fuga nulla modi doloremque maxime.
52 |
53 |
54 |
55 |
56 |
57 |
Company Name
58 |
Job Title
59 |
60 |
61 | Jan 2013 - Jan 2014
62 |
63 |
64 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Illum voluptate iure quasi inventore doloremque exercitationem maiores libero corrupti magnam praesentium voluptatem facere optio recusandae sit debitis tenetur beatae, voluptatibus itaque magni, est. Fugiat nostrum nemo fuga nulla modi doloremque maxime.
65 |
66 |
67 |
68 |
69 |
70 |
Company Name
71 |
Job Title
72 |
73 |
74 | Jan 2013 - Jan 2014
75 |
76 |
77 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Illum voluptate iure quasi inventore doloremque exercitationem maiores libero corrupti magnam praesentium voluptatem facere optio recusandae sit debitis tenetur beatae, voluptatibus itaque magni, est. Fugiat nostrum nemo fuga nulla modi doloremque maxime.
78 |
79 |
80 |
81 |
82 |
83 | Education
84 |
85 |
86 |
87 |
88 |
University of School
89 |
Bachelors of Science in Some Field
90 |
91 |
92 | Jan 2013 - Jan 2014
93 |
94 |
95 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laudantium blanditiis architecto, ipsum excepturi ducimus asperiores, eius necessitatibus porro harum? Reiciendis, vel. Voluptates dolorum soluta minima iste iusto, quae aliquam magni, dolores labore, placeat reiciendis! Corrupti, in, ullam! Repudiandae, quibusdam aliquid!
96 |
97 |
98 |
99 |
100 |
101 | Skills
102 |
103 |
104 |
105 | Skill One
106 | Skill Two
107 | Skill Three
108 | Skill Four
109 |
110 |
111 |
112 | Skill One
113 | Skill Two
114 | Skill Three
115 | Skill Four
116 |
117 |
118 |
119 | Skill One
120 | Skill Two
121 | Skill Three
122 | Skill Four
123 |
124 |
125 |
126 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Minima, obcaecati aut quas et voluptatibus dolorem similique, voluptate, totam, corrupti placeat molestiae distinctio consectetur. Et dolorum, eos, expedita debitis sed explicabo.
127 |
128 |
129 |
130 |
131 | References
132 |
133 |
Refernce Name, Job Title
134 |
Company Name
135 |
149 |
150 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Temporibus, quam. Earum suscipit aliquid eius nostrum iste reprehenderit nesciunt, ratione natus consectetur voluptatum magni, culpa in nihil veniam est quod dolorum sunt facere odio quibusdam corporis adipisci fugiat quasi, consequuntur. Debitis?
151 |
152 |
153 |
154 |
155 |
156 |
157 |
--------------------------------------------------------------------------------
/resume/routes.py:
--------------------------------------------------------------------------------
1 | from flask import Flask,make_response,render_template,url_for,flash,redirect,request,abort
2 | from resume.forms import RegistrationForm,LoginForm,account,PostingForm,resumebuilder,useredu,userexp,userpro,usersk,achieve,requestresetform,resetpassword
3 | from resume.models import UserModel,education,experience,projects,userdetails,skills,achievements
4 | from resume import app,db, bcrypt , mail
5 | from flask_login import login_user,current_user,logout_user,login_required
6 | from flask_mail import Message
7 | import secrets,os
8 | from PIL import Image
9 | import pdfkit
10 | from dotenv import load_dotenv
11 | load_dotenv()
12 |
13 | title = "Posts"
14 |
15 | @app.route("/")
16 | def hello():
17 | return render_template("home.html")
18 |
19 |
20 |
21 |
22 | @app.route("/register",methods=['GET','POST'])
23 | def register():
24 | if current_user.is_authenticated:
25 | return redirect(url_for('hello'),code=301)
26 | form = RegistrationForm()
27 | if form.validate_on_submit():
28 | hashed = bcrypt.generate_password_hash(form.password.data).decode('utf-8')
29 | new_user = UserModel(username=form.username.data,email=form.email.data,password=hashed)
30 | new_user.add_to_database()
31 | flash(f'Account Created for {form.username.data}!','success')
32 | return redirect(url_for('login'),code=302)
33 | return render_template('register.html',title='Register',form=form)
34 |
35 | @app.route("/login",methods=['GET','POST'])
36 | def login():
37 | if current_user.is_authenticated:
38 | return redirect(url_for('hello'),code=301)
39 | form = LoginForm()
40 | if form.validate_on_submit():
41 | logged = UserModel.find_by_email(form.email.data)
42 | if logged and bcrypt.check_password_hash(logged.password,form.password.data):
43 | login_user(logged,remember=form.remember.data)
44 | next_page = request.args.get('next')
45 | if next_page:
46 | return redirect(next_page,code=302)
47 | else:
48 | return redirect(url_for('hello'),code=301)
49 | else:
50 | flash('Login unsuccessful')
51 |
52 | return render_template('login.html',title='Login',form=form)
53 |
54 |
55 | @app.route("/logout")
56 | def logout():
57 | logout_user()
58 | return redirect(url_for("hello"),code=302)
59 |
60 | def save_picture(form_picture):
61 | random_hex = secrets.token_hex(8)
62 | _,ext = os.path.splitext(form_picture.filename)
63 | pic = random_hex + ext
64 | path = os.path.join(app.root_path,'static/profiles',pic)
65 |
66 | output_size = (125,125)
67 | img = Image.open(form_picture)
68 | img.thumbnail(output_size)
69 | img.save(path)
70 |
71 | return pic
72 |
73 |
74 |
75 | @app.route("/accounts",methods=['GET','POST'])
76 | @login_required #checks if user logged in or not and then allows access
77 | def accounts():
78 | form= account()
79 | if form.validate_on_submit():
80 | if form.picture.data:
81 | pic_file = save_picture(form.picture.data)
82 | current_user.picture = pic_file
83 | current_user.username = form.new_username.data
84 | current_user.email = form.new_email.data # Update current user values
85 | db.session.commit()
86 | flash("Your Account has been updated","success")
87 | return redirect(url_for("accounts"),code=301)
88 | elif request.method == 'GET':
89 | form.new_username.data = current_user.username
90 | form.new_email.data = current_user.email
91 | image_file = url_for('static',filename='profiles/'+ current_user.image_file)
92 | return render_template("account.html",title="Account",image_file=image_file,form=form)
93 |
94 |
95 | @app.route("/resume/new",methods=['GET','POST'])
96 | @login_required
97 | def post():
98 | form = resumebuilder()
99 | if form.validate_on_submit():
100 | usr = userdetails(name=form.name.data,email=form.email.data,designation=form.designation.data,phoneno=form.phoneno.data,profile=form.profile.data,details=current_user)
101 | db.session.add(usr)
102 | db.session.commit()
103 | print(form.name.data,form.email.data,form.designation.data)
104 | return redirect(url_for("postedu"),code=301)
105 | return render_template("posts.html",title="New Resume",form=form)
106 |
107 | #### Separation#####
108 | @app.route("/resume/new/education",methods=['GET','POST'])
109 | @login_required
110 | def postedu():
111 | form = useredu()
112 | eduadded = education.query.filter_by(edu=current_user).all()
113 | if form.validate_on_submit():
114 | edu = education(name=form.college.data,start=form.start.data,end=form.end.data,cgpa=form.cgpa.data,edu=current_user)
115 | print(form.college.data,form.start.data,form.end.data)
116 | db.session.add(edu)
117 | db.session.commit()
118 | #print(form.company.data,form.position.data)
119 | return redirect(url_for("postedu"),code=301)
120 | return render_template("education.html",title="Education",form=form,eduadded=eduadded)
121 |
122 |
123 | @app.route("/resume/new/experience",methods=['GET','POST'])
124 | @login_required
125 | def postexperience():
126 | form = userexp()
127 | expadded = experience.query.filter_by(exp=current_user).all()
128 | if form.validate_on_submit():
129 | exp = experience(company=form.company.data,position=form.position.data,startexp=form.startexp.data,endexp=form.endexp.data,content=form.content.data,exp=current_user)
130 | db.session.add(exp)
131 | db.session.commit()
132 | return redirect(url_for("postexperience"),code=301)
133 | return render_template("experience.html",title="Experience",form=form,expadded=expadded)
134 |
135 |
136 | @app.route("/resume/new/projects",methods=['GET','POST'])
137 | @login_required
138 | def postprojects():
139 | form = userpro()
140 | proadded = projects.query.filter_by(pro=current_user).all()
141 | if form.validate_on_submit():
142 | pro = projects(projectname=form.projectname.data,startpro=form.startpro.data,endpro=form.endpro.data,description=form.description.data,url=form.url.data,pro=current_user)
143 | db.session.add(pro)
144 | db.session.commit()
145 | return redirect(url_for("postprojects"),code=301)
146 | return render_template("projects.html",title="Projects",form=form,proadded=proadded)
147 |
148 | @app.route("/resume/new/skills",methods=['GET','POST'])
149 | @login_required
150 | def postskills():
151 | form = usersk()
152 | skillsadded = skills.query.filter_by(skill=current_user).all()
153 | if form.validate_on_submit():
154 | sk = skills(skillname=form.skillname.data,skill=current_user)
155 | db.session.add(sk)
156 | db.session.commit()
157 | return redirect(url_for("postskills"),code=301)
158 | #print(form.skillname.data)
159 |
160 | return render_template("skills.html",title="Skills",form=form,skillsadded=skillsadded)
161 |
162 |
163 | @app.route("/resume/new/acheive",methods=['GET','POST'])
164 | @login_required
165 | def postacheive():
166 | form = achieve()
167 | achadded = achievements.query.filter_by(ach=current_user).all()
168 | if form.validate_on_submit():
169 | acheive = achievements(achname=form.achname.data,achdesc=form.achname.data,ach=current_user)
170 | db.session.add(acheive)
171 | db.session.commit()
172 | return redirect(url_for("postskills"),code=301)
173 | #print(form.skillname.data)
174 |
175 | return render_template("acheive.html",title="Acheivements",form=form,achadded=achadded)
176 |
177 |
178 |
179 |
180 | #### End Separation ####
181 |
182 |
183 |
184 |
185 | ### Generate Resume
186 |
187 |
188 | @app.route("/resume",methods=["GET","POST"])
189 | @login_required
190 | def resumeview():
191 | edu = education.query.filter_by(edu=current_user).all()
192 | exp = experience.query.filter_by(exp=current_user).all()
193 | pro = projects.query.filter_by(pro=current_user).all()
194 | usr = userdetails.query.filter_by(details=current_user).first()
195 | skillsadded = skills.query.filter_by(skill=current_user).all()
196 | achmade = achievements.query.filter_by(ach=current_user).all()
197 |
198 |
199 | image_file = url_for('static',filename='profiles/'+ current_user.image_file)
200 |
201 | return render_template("resume.html",edu=edu,exp=exp,pro=pro,usr=usr,sk=skillsadded,achmade=achmade,image_file=image_file)
202 |
203 |
204 |
205 |
206 | @app.route("/download",methods=["GET"])
207 | @login_required
208 | def downloadpdf():
209 | edu = education.query.filter_by(edu=current_user).all()
210 | exp = experience.query.filter_by(exp=current_user).all()
211 | pro = projects.query.filter_by(pro=current_user).all()
212 | usr = userdetails.query.filter_by(details=current_user).first()
213 | skillsadded = skills.query.filter_by(skill=current_user).all()
214 | achmade = achievements.query.filter_by(ach=current_user).all()
215 |
216 | image_file = url_for('static',filename='profiles/'+ current_user.image_file)
217 | css = ["resume/static/resume.css","resume/static/main.css"]
218 | rendered = render_template("resume.html",edu=edu,exp=exp,pro=pro,usr=usr,sk=skillsadded,achmade=achmade,image_file=image_file)
219 | pdf =pdfkit.from_string(rendered,False,css=css)
220 |
221 | response = make_response(pdf)
222 | response.headers["Content-Type"] = "application/pdf"
223 | response.headers["Content-Disposition"] = "attachement; filename=resume.pdf"
224 |
225 | return response
226 |
227 |
228 |
229 |
230 | ### Updating and deleting
231 | #acheivments
232 | @app.route("/resume/new/acheive//update",methods=["GET","POST"])
233 | def update_acheive(acheive_id):
234 | achview = achievements.query.get_or_404(acheive_id)
235 |
236 | form = achieve()
237 | if form.validate_on_submit():
238 | achview.achname = form.achname.data
239 | achview.achdesc = form.achdesc.data
240 | db.session.commit()
241 | flash('Your acheivement has been updated!', 'success')
242 | return redirect(url_for('postacheive'),code=301)
243 | elif request.method == 'GET':
244 | form.achname.data= achview.achname
245 | form.achdesc.data = achview.achdesc
246 | return render_template("acheive.html",title="Update Acheivement",form=form)
247 |
248 | @app.route("/resume/new/acheive//delete",methods=["GET","POST"])
249 | def delete_acheive(acheive_id):
250 | achview = achievements.query.get_or_404(acheive_id)
251 |
252 | db.session.delete(achview)
253 | db.session.commit()
254 | flash('Your post has been deleted!', 'success')
255 | return redirect(url_for('postacheive'),code=301)
256 |
257 | #education
258 | @app.route("/resume/new/education//update",methods=["GET","POST"])
259 | def update_edu(education_id):
260 | eduview = education.query.get_or_404(education_id)
261 |
262 | form = useredu()
263 | if form.validate_on_submit():
264 | eduview.name = form.college.data
265 | eduview.start = form.start.data
266 | eduview.end = form.end.data
267 | eduview.cgpa = form.cgpa.data
268 | db.session.commit()
269 | flash('Your education details have been updated!', 'success')
270 | return redirect(url_for('postedu'),code=301)
271 | elif request.method == 'GET':
272 | form.college.data= eduview.name
273 | form.start.data = eduview.start
274 | form.end.data = eduview.end
275 | form.cgpa.data = eduview.cgpa
276 | return render_template("education.html",title="Update Education",form=form)
277 |
278 | @app.route("/resume/new/education//delete",methods=["GET","POST"])
279 | def delete_edu(education_id):
280 | eduview = education.query.get_or_404(education_id)
281 |
282 | db.session.delete(eduview)
283 | db.session.commit()
284 | flash('Your education detail post has been deleted!', 'success')
285 | return redirect(url_for('postedu'),code=301)
286 |
287 |
288 | #Experience
289 | @app.route("/resume/new/experience//update",methods=["GET","POST"])
290 | def update_exp(experience_id):
291 | expview = experience.query.get_or_404(experience_id)
292 |
293 | form = userexp()
294 | if form.validate_on_submit():
295 | expview.company = form.company.data
296 | expview.startexp = form.startexp.data
297 | expview.endexp = form.endexp.data
298 | expview.content = form.content.data
299 | db.session.commit()
300 | flash('Your education details have been updated!', 'success')
301 | return redirect(url_for('postexperience'),code=301)
302 | elif request.method == 'GET':
303 | form.company.data= expview.company
304 | form.startexp.data = expview.startexp
305 | form.endexp.data = expview.endexp
306 | form.content.data = expview.content
307 | return render_template("experience.html",title="Update Work Experience",form=form)
308 |
309 | @app.route("/resume/new/experience//delete",methods=["GET","POST"])
310 | def delete_exp(experience_id):
311 | expview = experience.query.get_or_404(experience_id)
312 |
313 | db.session.delete(expview)
314 | db.session.commit()
315 | flash('Your experience detail post has been deleted!', 'success')
316 | return redirect(url_for('postexp'),code=301)
317 |
318 |
319 | #Projects
320 | @app.route("/resume/new/projects//update",methods=["GET","POST"])
321 | def update_pro(project_id):
322 | proview = projects.query.get_or_404(project_id)
323 |
324 | form = userpro()
325 | if form.validate_on_submit():
326 | proview.projectname = form.projectname.data
327 | proview.startpro = form.startpro.data
328 | proview.endpro = form.endpro.data
329 | proview.description = form.description.data
330 | proview.url = form.url.data
331 | db.session.commit()
332 | flash('Your project details have been updated!', 'success')
333 | return redirect(url_for('postprojects'),code=301)
334 | elif request.method == 'GET':
335 | form.projectname.data= proview.projectname
336 | form.startpro.data = proview.startpro
337 | form.endpro.data = proview.endpro
338 | form.description.data = proview.description
339 | form.url.data = proview.url
340 | return render_template("projects.html",title="Update Projects",form=form)
341 |
342 | @app.route("/resume/new/projects//delete",methods=["GET","POST"])
343 | def delete_pro(project_id):
344 | proview = projects.query.get_or_404(project_id)
345 |
346 | db.session.delete(proview)
347 | db.session.commit()
348 | flash('Your project detail post has been deleted!', 'success')
349 | return redirect(url_for('postprojects'),code=301)
350 |
351 |
352 |
353 | #### Reset Password ####
354 | def send_reset_pass(user):
355 | email_id= os.environ["MAIL_USERNAME"]
356 | token = UserModel.reset_token()
357 | msg= Message("Password Reset Request",
358 | sender="{}".format(email_id),recipients=[user.email])
359 |
360 | msg.body = f''' To reset your password , visit the following link:
361 | { url_for('token_reset',token=token,_external=True) }
362 |
363 | If you did not make this request then ignore this message
364 | '''
365 |
366 | mail.send(msg)
367 |
368 |
369 | @app.route("/reset_account",methods=["GET","POST"])
370 | def request_reset():
371 | if current_user.is_authenticated:
372 | return redirect(url_for('hello'),code=301)
373 | form = requestresetform()
374 | if form.validate_on_submit():
375 | req_user = UserModel.find_by_email(form.email.data)
376 | send_reset_pass(req_user)
377 | flash("Email has been sent with instructions to Reset Password")
378 | return redirect(url_for("hello"),code=301)
379 |
380 | return render_template("reset.html",title="Reset Request Form",form=form)
381 |
382 | @app.route("/reset_account/",methods=["GET","POST"])
383 | def token_reset(token):
384 | if current_user.is_authenticated:
385 | return redirect(url_for('hello'),code=302)
386 | user_req = UserModel.verify_token(token)
387 | if user_req is None:
388 | flash("Token is Invalid or Expired","warning")
389 | return redirect(url_for('request_reset'),code=307)
390 | form = resetpassword()
391 | if form.validate_on_submit():
392 | hashed = bcrypt.generate_password_hash(form.password.data).decode('utf-8')
393 | user_req.password = hashed
394 | user_req.add_to_database()
395 | flash(f'Password Updated ','success')
396 | return redirect(url_for('login'),code=302)
397 | return render_template("reset_password.html",title="Reset Password",form=form)
--------------------------------------------------------------------------------