- Readme
- How to configure the enviroment for the project
- How to run the project
- Structure of the project
- BACKLOG
- FAQ
- I want to change how something is displayed in the webpage!
- How does the
templates/
directory work? - How does the static/css folder work?
- What is inside
datasets/
? - How are the sponsors displayed?
- How can I modify the FAQ in the homepage?
- How does the logger work?
- How to use the logger inside the classes/ folder
- What logs do i have? How do them work?
- Whats the purpose of the classes inside classes/ ?
- What is babel / How does translation work / Why do i see a lot of underscores _() everywhere / How do i change translation/language?
- How are categories translated in sponsors_generator?
- How do i build the project
- Helpful information
This is the repository for Nerdearla, the Sysarmy event
- Create a virtual enviroment in a folder called
.venv/
runningpython -m venv .venv/
- Source that virtual enviroment with
source .venv/bin/activate
, orsource .venv/Scripts/activate
if you are in windows. If you are using powershell, replace / with \ - Install the packages in
requirements.txt
runningpip install -r requirements.txt
- Source the python virtual enviroment with
source .venv/bin/active
- Run flask with
flask run
- Enjoy
.
└── app: # the files used for the webapp
├── static # static files (the css, the fonts, the images and the javascript files)
│ ├── css # styling
│ ├── fonts # fonts that are not in Google Fonts
│ ├── img # images
│ └── js # javascript code
└── templates # jinja-html code
└── components # jinja-html components
- Navbar
- Basic structure and operation
- Responsiveness
- Final styling
- Hero
- Basic structure and operation
- Responsiveness
- Final styling
- About
- Basic structure and operation
- Responsiveness
- Final styling
- Countdown
- Basic structure and operation
- Responsiveness
- Final styling
- Nerdearla in numbers (statistics)
- Basic structure and operation
- Responsiveness
- Final styling
- Speakers
- Basic structure and operation
- Responsiveness
- Final styling
- Ubication
- Basic structure and operation
- Responsiveness
- Final styling
- FAQ
- Basic structure and operation
- Responsiveness
- Final styling
- Contact
- Basic structure and operation
- Responsiveness
- Final styling
- Footer
- Basic structure and operation
- Responsiveness
- Final styling
- Index
- Basic structure and operation
- Responsiveness
- Final styling
- Sponsors
- Basic structure and operation
- Responsiveness
- Final styling
- Code of Conduct
- Basic structure and operation
- Responsiveness
- Final styling
- Agenda
- Basic structure and operation
- Responsiveness
- Final styling
- Error pages
- 404 page
- Logging
- Error handling
- routes.py
- filters.py
- Multilanguage support
- en
- es
First, try to look which component defines that part of the webpage (if you dont know how that works, see the "How does the templates/
directory work?" section).
Next, try to identify if what you are trying to modify is related to the data that is shown (backend related, probably inside routes.py), or how the data is shown (frontend related, probably inside your component or a .html file isnide templates/)
If the problem is backend related, try to look for the route that renders the template you are looking at.
For example, if you want to change how data is collected at the sponsors page, look for the definition of the sponsors/
route
Every file inside templates/
(except base.html
) is rendered using render_template in an specific route defined in routes.py
.
Each file inherits from base.html
, and uses the components in components/
using {% include "components/component_name_here.html" %}
This is done for the purpose of clean and tidy code
An example of a component could be:
<!-- This file holds the section that talks about the speakers -->
<section id="speakers">
<!-- Get the css file using jinja-html syntax -->
<link rel="stylesheet" href="{{ url_for('static', filename='css/speakers.css') }}" />
<!-- Main content of the html -->
<div class="container">
<h1>Nuestros speakers</h1>
<div>
<p>Our speakers are great because...</p>
</div>
</div>
<!-- Get the javascript file using jinja-html syntax -->
<script src="{{ url_for('static', filename='js/speakers.js') }}"></script>
</section>
The CSS folder has two types of .css files
Some css files are asociated to html files inside templates/ (or templates/components).
For example, hero.css
is used in the hero.html
component, or sponsors.css
is used in sponsors.html
Other files are for general usage, like style.css
, which defines general css to be used in the whole project.
fonts.css
loads the fonts from static/fonts/trama_backgrounds.css
defines classes for generating backgrounds using the tramas inside img/svg/tramasvariables.css
defines global variables, for example the Nerderla colors.
Datasets is a folder which contains files that modify what and how data is displayed in the page. The purpose of this folder was to hold files that modify the webpage without needing to touch the code
For example, the sponsors_config.json
file sets the order in which the sponsors are displayed in sponsors.html
, among other things.
This json file is accessed using read_json_file()
from functions.py
The sponsors are defined inside sponsors.csv
The sponsors are defined in datasets/sponsors.csv
sponsors.csv
should look like this:
name ,category ,file ,link
cognizant ,diamond ,cognizant.png ,https://sysar.my/cognizantsoftvision
icbc ,adamantium ,icbc.png ,https://www.icbc.com.ar
openqube ,adamantium ,openqube.png ,https://openqube.io/
google cloud platform ,adamantium ,googlecloud.png ,https://cloud.google.com/
rappi ,silver ,rappi.png ,https://rappi.io/
rappi ,silver ,rappi.png ,https://rappi.io/
rappi ,silver ,rappi.png ,https://rappi.io/
You can use whitespaces because the sponsors/
route automatically removes trailing whitespaces
In the datasets/
folder, create or modify a faq.json
file with FAQ's like the following:
[
{
"title": "Que es Nerdearla",
"description": "Nerdearla es lorem ipsum bla bla bla"
},
{
"title": "Cuando se hace la Nerdearla",
"description": "La respuesta esta en tu corazon"
},
{
"title": "Cúanto sale?",
"description": "Paga Jolo"
}
]
Generated by chatGPT: To use the logger in each file of your Flask project, you can follow these steps:
- Create a separate file to configure the logger. Let's call it
logger_config.py
.
import os
import logging
from flask import Flask
def configure_logger(app: Flask):
"""
Configure and return a logger instance with different handlers for console, Werkzeug logs,
general logs, and error logs.
This function sets up a logger with multiple handlers to direct log messages based on their
level and source. Non-Werkzeug log messages at INFO level and higher are sent to the console
and written to the 'general.log' file. Only Werkzeug logs are written to the 'werkzeug.log' file.
ERROR and higher level non-Werkzeug messages are captured in the 'errors.log' file.
Args:
app (Flask): The Flask application object.
Returns:
logging.Logger: The configured logger instance.
Note:
Make sure to call this function after creating the Flask application object.
"""
# Create a logger instance
logger = app.logger
# Set the log level for the logger
logger.setLevel(logging.DEBUG)
# Remove existing console handler if present
for handler in logger.handlers:
if isinstance(handler, logging.StreamHandler):
logger.removeHandler(handler)
# Create a console handler for non-Werkzeug logs
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
console_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
console_handler.setFormatter(console_formatter)
logger.addHandler(console_handler)
# Create the 'logs/' folder if it doesn't exist
if not os.path.exists("logs/"):
try:
os.makedirs("logs/")
except PermissionError:
logger.error("Failed to create 'logs/' folder. Check permissions.")
# Create a file handler for Werkzeug logs
try:
werkzeug_handler = logging.FileHandler("logs/werkzeug.log")
werkzeug_handler.setLevel(logging.DEBUG) # Set the desired level for Werkzeug logs
werkzeug_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
werkzeug_handler.setFormatter(werkzeug_formatter)
werkzeug_logger = logging.getLogger("werkzeug")
werkzeug_logger.setLevel(logging.DEBUG) # Set the desired level for Werkzeug logs
werkzeug_logger.addHandler(werkzeug_handler)
except PermissionError:
logger.error("Failed to create 'werkzeug.log' file. Check permissions.")
# Create a file handler for general logs (INFO and higher, excluding Werkzeug and ERROR level)
try:
general_handler = logging.FileHandler("logs/general.log")
general_handler.setLevel(logging.INFO)
general_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
general_handler.setFormatter(general_formatter)
logger.addHandler(general_handler)
except PermissionError:
logger.error("Failed to create 'general.log' file. Check permissions.")
# Create a file handler for error logs (ERROR and higher, excluding Werkzeug)
try:
error_handler = logging.FileHandler("logs/errors.log")
error_handler.setLevel(logging.ERROR)
error_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
error_handler.setFormatter(error_formatter)
error_logger = logging.getLogger("errors")
error_logger.setLevel(logging.ERROR) # Set the desired level for error logs
error_logger.addHandler(error_handler)
except PermissionError:
logger.error("Failed to create 'errors.log' file. Check permissions.")
return logger
- In the
__init__.py
file of your app folder, import theconfigure_logger
function fromlogger_config
and call it, passing the app object.
from flask import Flask
from logger_config import configure_logger
app = Flask(__name__)
# Configure the logger
configure_logger(app)
# Import routes and functions
from app import routes
from app import functions
- In the other files within the app folder (
routes.py
) import the logger and use it as needed.
For example, in routes.py
:
from flask import request
from app import app
logger = app.logger
@app.route('/')
def index():
logger.info('Processing index request...')
# Your code here
By importing the app object and logger instance from __init__.py
in each file, you can access and use the logger across different modules and files within your Flask project.
Generated by chatGPT
To use the app logger in the classes located within the classes folder of your Flask project, you can follow these steps:
- In the
__init__.py
file located inside the classes folder, import the logger from the__init__.py
file of the app folder.
from app import app
logger = app.logger
- In the other Python files within the classes folder (
DatasetsUtils.py
andSponsorProcessor.py
), import the logger and use it as needed.
For example, in DatasetsUtils.py
:
from classes import logger
class DatasetsUtils:
def some_method(self):
logger.info('This is an info message from DatasetsUtils')
# Your code here
And in SponsorProcessor.py
:
from classes import logger
class SponsorProcessor:
def some_method(self):
logger.debug('This is a debug message from SponsorProcessor')
# Your code here
By importing the logger from the app module's __init__.py
file in the __init__.py
file of the classes folder, you can access and use the app logger across the classes within the classes folder.
- Non-Werkzeug log messages at INFO level and higher are sent to the console and written to the 'general.log' file.
- ERROR and higher level non-Werkzeug messages are captured in the 'errors.log' file.
- Werkzeug-only logs are written to the 'werkzeug.log' file.
The idea is to divide the code to make it cleaner and easier to use
What is babel / How does translation work / Why do i see a lot of underscores _() everywhere / How do i change translation/language?
Its flask babel https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-xiii-i18n-and-l10n
The tutorial shows you how to do scripts integrated with flask, but i hadnt read that part at the moment of creating this scripts
When you replace all text in html files with {{ _('') }}
,
-
run
sh scripts/extract_messages.sh
(if its not running, runchmod +x extract_messages.sh
to give it permissions to run). -
FIRST TIME run
sh scripts/generate_language_catalog.sh en
. Replaceen
with the language code you want. In this project, we are translating to english, so we use en. If you get a "Please provide a language parameter." error, you forgot to pass the language as a parameter, so it means you forgot theen
when running the file. -
After the first time, use
scripts/update_language_catalog.sh
instead ofscripts/generate_language_catalog.sh
-
Use chatGPT to fill the messages.po file or do it by hand
-
Run
scripts/generate_language_output.sh
-
Stop flask from running and run it again. Flask does not seem to notice when the messages.po file changes, so you wont see the changes until you reload the server.
Its currently hardcoded like this into sponsors_generator.html
Remember that categories are automatically converted into lowercase when retrieved from the csv
{# HACK: This hardcoded thing here translates special categories titles into other languages #}
{% if lang_code == "en" %}
{# HACK: The special categories are here #}
{% if category == "nos apoyan" %}
{% set category = "supported by" %}
{% endif %}
{% if category == "comunidades" %}
{% set category = "communities" %}
{% endif %}
{% endif %}
<h1> {{category}} </h1>
- Run python freeze.py
Understanding Flask: https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world (READ FIRST)
Understanding multilanguage: https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-xiii-i18n-and-l10n and https://medium.com/@nicolas_84494/flask-create-a-multilingual-web-application-with-language-specific-urls-5d994344f5fd
Jinja formatter: https://stackoverflow.com/questions/60175608/visual-studio-code-and-jinja-templates
Using Flask, Flask-WTF, and AJAX for forms: If you are confused about how contact.html, submit_contact/ and contact.js work, watch this video: https://www.youtube.com/watch?v=QKcVjdLEX_s and then read this blog https://blog.carsonevans.ca/2019/08/20/validating-ajax-requests-with-wtforms-in-flask/
Understanding logging: https://www.youtube.com/watch?v=-ARI4Cz-awo and https://www.youtube.com/watch?v=jxmzY9soFXg
You should add this line in your User settings.json.
The reason for that is because that specific line is not available for workspace specific configuration, which is defined in the .vscode/settings.json
file
{
"css.enabledLanguages": ["jinja-html", "django-html", "html"]
}