diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000000..58ea154d7e --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,21 @@ +# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/python-3/.devcontainer/base.Dockerfile + +# [Choice] Python version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.10, 3.9, 3.8, 3.7, 3.6, 3-bullseye, 3.10-bullseye, 3.9-bullseye, 3.8-bullseye, 3.7-bullseye, 3.6-bullseye, 3-buster, 3.10-buster, 3.9-buster, 3.8-buster, 3.7-buster, 3.6-buster +ARG VARIANT="3.10-bullseye" +FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} + +# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10 +ARG NODE_VERSION="none" +RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi + +# [Optional] If your pip requirements rarely change, uncomment this section to add them to the image. +# COPY requirements.txt /tmp/pip-tmp/ +# RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \ +# && rm -rf /tmp/pip-tmp + +# [Optional] Uncomment this section to install additional OS packages. +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends + +# [Optional] Uncomment this line to install global node packages. +# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..e6ebc7bd70 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,51 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/python-3 +{ + "name": "Python 3", + "build": { + "dockerfile": "Dockerfile", + "context": "..", + "args": { + // Update 'VARIANT' to pick a Python version: 3, 3.10, 3.9, 3.8, 3.7, 3.6 + // Append -bullseye or -buster to pin to an OS version. + // Use -bullseye variants on local on arm64/Apple Silicon. + "VARIANT": "3.10-bullseye", + // Options + "NODE_VERSION": "lts/*" + } + }, + + // Set *default* container specific settings.json values on container create. + "settings": { + "python.defaultInterpreterPath": "/usr/local/bin/python", + "python.linting.enabled": true, + "python.linting.pylintEnabled": true, + "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8", + "python.formatting.blackPath": "/usr/local/py-utils/bin/black", + "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf", + "python.linting.banditPath": "/usr/local/py-utils/bin/bandit", + "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8", + "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy", + "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle", + "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", + "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint" + }, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-python.python", + "ms-python.vscode-pylance" + ], + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "pip3 install --user -r requirements.txt", + + // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode", + "features": { + "git": "latest" + } +} diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..6211dcfb80 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +.devcontainer +.git + + +*.pyc + +.gitignore +.env.example + +static \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000000..3bef7d0d1c --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +URL_API_GITHUB= +SECRET_KEY= +DEBUG= +DATABASE_URL= \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..b167770337 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.env + +*.pyc + +db.sqlite3 + +staticfiles/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..cc67606f33 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "python.linting.pylintEnabled": true, + "python.linting.enabled": true +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..64f8d3252c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.10 + +WORKDIR /app + +COPY requirements.txt /app/ + +RUN apt update -y + +RUN pip install -r requirements.txt + +COPY . . + +RUN python manage.py collectstatic + +CMD ["gunicorn", "--bind", ":8000", "core.wsgi"] \ No newline at end of file diff --git a/Procfile b/Procfile new file mode 100644 index 0000000000..637c8795d5 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: gunicorn core.wsgi \ No newline at end of file diff --git a/README.md b/README.md index 3f1e493650..5106e80ffb 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,33 @@ -# Desafio técnico para desenvolvedores +# DEV HIRING CHALLENGE - ATELIWARE -Construa uma nova aplicação, utilizando o framework de sua preferência (Ruby on Rails, Elixir Phoenix, Python Django ou Flask, NodeJS Sails, Java Spring, ASP.NET ou outro), a qual deverá conectar na API do GitHub e disponibilizar as seguintes funcionalidades: +Uma aplicação desenvolvida para consumo da api do github, listando os principais repositórios das seguintes linguagens: Python, Javascript, Rust, Elixir e Ruby. -- Botão para buscar e armazenar os repositórios destaques de 5 linguagens à sua escolha; -- Listar os repositórios encontrados; -- Visualizar os detalhes de cada repositório. +## Instruções -Alguns requisitos: +### Dependências +- docker -- Deve ser uma aplicação totalmente nova; -- A solução deve estar em um repositório público do GitHub; -- A aplicação deve armazenar as informações encontradas; -- Utilizar PostgreSQL, MySQL ou SQL Server; -- O deploy deve ser realizado, preferencialmente, no Heroku, AWS ou no Azure; -- A aplicação precisa ter testes automatizados; -- Preferenciamente dockerizar a aplicação; -- Por favor atualizar o readme da aplicação com passo a passo com instrução para subir o ambiente. +### Arquivos de configuração + Crie um arquivo .env baseado em .env.example com a url de acesso ao banco de dados, chave secreta, a url da api do github e se será debugado o código. -Quando terminar, faça um Pull Request neste repo e avise-nos por email. +### Container do banco de dados + docker network create postgres + docker run -d --name postgres --network=postgres -e "POSTGRES_PASSWORD=teste" -p 5432:5432 -v data:/var/lib/postgresql/data postgres -**IMPORTANTE:** se você não conseguir finalizar o teste, por favor nos diga o motivo e descreva quais foram as suas dificuldades. Você pode também sugerir uma outra abordagem para avaliarmos seus skills técnicos, vender seu peixe, mostrar-nos do que é capaz. +### Criando banco de dados + docker exec -t postgres psql -U postgres -c "CREATE DATABASE dev_hiring_challenge" + +### Buildando container da aplicação + docker build -t ateliware/dev-hiring-challenge . + +### Executando container + docker run -p 8000:8000 --network postgres --name dev-hiring-challenge ateliware/dev-hiring-challenge + +### Aplicando migrations + docker exec -t dev-hiring-challenge python manage.py migrate + +### Testando aplicação + docker exec -t dev-hiring-challenge python manage.py test + +### URL do ambiente de produção +https://blooming-shelf-11188.herokuapp.com/ \ No newline at end of file diff --git a/app.json b/app.json new file mode 100644 index 0000000000..d0507e887c --- /dev/null +++ b/app.json @@ -0,0 +1,22 @@ +{ + "name": "Dev Hiring Challenge - Ateliware", + "description": "Aplicação web responsável pela a listagem de repositórios do github das principais linguagens.", + "image": "heroku/python", + "repository": "https://github.com/vitorgripa/dev-hiring-challenge", + "keywords": ["python", "django" ], + "addons": [ "heroku-postgresql" ], + "env": { + "SECRET_KEY": { + "description": "The secret key for the Django application.", + "generator": "secret" + } + }, + "environments": { + "test": { + "scripts": { + "test-setup": "python manage.py collectstatic --noinput", + "test": "python manage.py test" + } + } + } + } \ No newline at end of file diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/core/asgi.py b/core/asgi.py new file mode 100644 index 0000000000..d739b7ed4b --- /dev/null +++ b/core/asgi.py @@ -0,0 +1,7 @@ +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings') + +application = get_asgi_application() diff --git a/core/github/__init__.py b/core/github/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/core/github/apps.py b/core/github/apps.py new file mode 100644 index 0000000000..271a6ac50a --- /dev/null +++ b/core/github/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class GithubConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'core.github' diff --git a/core/github/constants.py b/core/github/constants.py new file mode 100644 index 0000000000..fba8d4219c --- /dev/null +++ b/core/github/constants.py @@ -0,0 +1,7 @@ +LINGUAGENS = ( + "Python", + "Javascript", + "Rust", + "Elixir", + "Ruby" +) \ No newline at end of file diff --git a/core/github/migrations/0001_initial.py b/core/github/migrations/0001_initial.py new file mode 100644 index 0000000000..5d8f4b8d31 --- /dev/null +++ b/core/github/migrations/0001_initial.py @@ -0,0 +1,46 @@ +# Generated by Django 4.0.1 on 2022-01-19 01:28 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Linguagem', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('nome', models.TextField()), + ], + options={ + 'verbose_name': 'Linguagem', + 'verbose_name_plural': 'Linguagens', + 'db_table': 'linguagens', + }, + ), + migrations.CreateModel( + name='Repositorio', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('nome', models.TextField()), + ('url', models.URLField()), + ('estrelas', models.IntegerField()), + ('pontos', models.FloatField()), + ('acompanhadores', models.IntegerField()), + ('lincenca', models.TextField()), + ('descricao', models.TextField()), + ('linguagem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='github.linguagem')), + ], + options={ + 'verbose_name': 'Repositório', + 'verbose_name_plural': 'Repositórios', + 'db_table': 'repositorios', + }, + ), + ] diff --git a/core/github/migrations/0002_alter_repositorio_descricao.py b/core/github/migrations/0002_alter_repositorio_descricao.py new file mode 100644 index 0000000000..5c74ef7ba7 --- /dev/null +++ b/core/github/migrations/0002_alter_repositorio_descricao.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.1 on 2022-01-19 03:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('github', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='repositorio', + name='descricao', + field=models.TextField(null=True), + ), + ] diff --git a/core/github/migrations/0003_initial_data.py b/core/github/migrations/0003_initial_data.py new file mode 100644 index 0000000000..c6eeb2bcc1 --- /dev/null +++ b/core/github/migrations/0003_initial_data.py @@ -0,0 +1,20 @@ +from django.db import migrations + +from core.github.constants import LINGUAGENS + + +def inserir_linguagens(apps, schema_editor): + Linguagem = apps.get_model("github", "Linguagem") + + for linguagem in LINGUAGENS: + Linguagem.objects.create(nome=linguagem) + + +class Migration(migrations.Migration): + dependencies = [ + ("github", "0002_alter_repositorio_descricao") + ] + + operations = [ + migrations.RunPython(inserir_linguagens) + ] diff --git a/core/github/migrations/0004_rename_lincenca_repositorio_licenca.py b/core/github/migrations/0004_rename_lincenca_repositorio_licenca.py new file mode 100644 index 0000000000..a4a3e469ca --- /dev/null +++ b/core/github/migrations/0004_rename_lincenca_repositorio_licenca.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.1 on 2022-01-20 01:50 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('github', '0003_initial_data'), + ] + + operations = [ + migrations.RenameField( + model_name='repositorio', + old_name='lincenca', + new_name='licenca', + ), + ] diff --git a/core/github/migrations/__init__.py b/core/github/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/core/github/models.py b/core/github/models.py new file mode 100644 index 0000000000..fd23035f37 --- /dev/null +++ b/core/github/models.py @@ -0,0 +1,42 @@ +from django.db.models import Model +from django.db.models import IntegerField +from django.db.models import TextField +from django.db.models import URLField +from django.db.models import ForeignKey +from django.db.models import FloatField +from django.db.models import CASCADE + +from django.db.models import Manager + + +class Linguagem(Model): + nome = TextField() + objects = Manager() + + def __str__(self): + return str(self.nome) + + class Meta: + db_table = "linguagens" + verbose_name = "Linguagem" + verbose_name_plural = "Linguagens" + + +class Repositorio(Model): + nome = TextField() + url = URLField() + estrelas = IntegerField() + pontos = FloatField() + acompanhadores = IntegerField() + licenca = TextField() + descricao = TextField(null=True) + linguagem = ForeignKey(Linguagem, on_delete=CASCADE) + objects = Manager() + + def __str__(self): + return str(self.nome) + + class Meta: + db_table = "repositorios" + verbose_name = "Repositório" + verbose_name_plural = "Repositórios" \ No newline at end of file diff --git a/core/github/services.py b/core/github/services.py new file mode 100644 index 0000000000..2d4170090b --- /dev/null +++ b/core/github/services.py @@ -0,0 +1,43 @@ +from requests import get + +from core.github.models import Linguagem as LinguagemModel + +from core.settings import URL_API_GITHUB + + +def fetch_repositorios_por_linguagem(linguagem: str): + + q = f"language:{linguagem.lower()}" + + parametros = { + "q": q + } + + url = f"{URL_API_GITHUB}/search/repositories" + + response = get(url, params=parametros) + + if response.status_code != 200: + response.raise_for_status() + + json = response.json() + + return json["items"] + + +def fetch_repositorios(): + linguagens = LinguagemModel.objects.all() + + for linguagem in linguagens: + repositorios = fetch_repositorios_por_linguagem(linguagem.nome) + + for repositorio in repositorios: + yield { + "nome": repositorio["name"], + "url": repositorio["html_url"], + "estrelas": repositorio["stargazers_count"], + "acompanhadores": repositorio["watchers"], + "pontos": repositorio["score"], + "descricao": repositorio["description"], + "linguagem": linguagem, + } diff --git a/core/github/templates/index.html b/core/github/templates/index.html new file mode 100644 index 0000000000..c631417640 --- /dev/null +++ b/core/github/templates/index.html @@ -0,0 +1,56 @@ + + + + + + + Repositórios GITHUB + + + + + {% load static %} + + + +
+ {% if repositorios %} +
    + {% for repositorio in repositorios %} +
  • + + {{ repositorio.nome }} + + + {{ repositorio.estrelas}} + + + {{ repositorio.descricao | truncatechars:40}} + + + {{ repositorio.acompanhadores}} + + + {{ repositorio.linguagem }} + +
  • + {% endfor %} +
+ < + {% else %} + Logo Ateliware + {% if linguagens %} +
    + {% for linguagem in linguagens %} +
  • {{linguagem.nome}}
  • + {% endfor %} +
+ {% endif %} +
+ {% csrf_token %} + +
+ {% endif %} +
+ + \ No newline at end of file diff --git a/core/github/tests.py b/core/github/tests.py new file mode 100644 index 0000000000..a245286cc7 --- /dev/null +++ b/core/github/tests.py @@ -0,0 +1,58 @@ +from requests import get + +from django.test import TestCase +from django.test import Client + +from core.settings import URL_API_GITHUB + +from core.github.models import Linguagem as LinguagemModel +from core.github.models import Repositorio as RepositorioModel + +from core.github.services import fetch_repositorios_por_linguagem + + +class Repositorio(TestCase): + + def setUp(self): + self.linguagem = LinguagemModel.objects.create(nome="Python") + + self.repositorio_data = { + "nome": "ateliware/dev-hiring-challenge", + "url": "https://github.com/ateliware/dev-hiring-challenge", + "estrelas": 600000, + "acompanhadores": 1000000, + "pontos": 10.0, + "licenca": "MSL - Mega Super Licença", + "descricao": "O melhor repositório que já existiu em toda a vida", + } + + self.cliente = Client() + + + def test_cadastro_repositorio(self): + RepositorioModel.objects.create( + **self.repositorio_data, + linguagem=self.linguagem + ) + + repositorios = RepositorioModel.objects.count() + + self.assertEqual(repositorios, 1) + + + def test_listagem_respotorios(self): + repositorios = fetch_repositorios_por_linguagem(self.linguagem.nome) + + self.assertNotEqual( + tuple(repositorios), 0 + ) + + def test_pagina_inicial(self): + response = self.cliente.get("/") + + self.assertEqual(response.status_code, 200) + + def test_pagina_repositorios(self): + response = self.cliente.post("/") + + self.assertEqual(response.status_code, 200) diff --git a/core/github/urls.py b/core/github/urls.py new file mode 100644 index 0000000000..abe6ae5ae2 --- /dev/null +++ b/core/github/urls.py @@ -0,0 +1,8 @@ +from django.urls import path + +from core.github.views import index + + +urlpatterns = [ + path("", view=index, name="index") +] diff --git a/core/github/views.py b/core/github/views.py new file mode 100644 index 0000000000..0789c48723 --- /dev/null +++ b/core/github/views.py @@ -0,0 +1,49 @@ +import logging + +from django.http.request import HttpRequest + +from django.shortcuts import render + +from core.github.models import Linguagem as LinguagemModel +from core.github.models import Repositorio as RepositorioModel + +from core.github.services import fetch_repositorios + + +logging.basicConfig(level=logging.INFO) + + +def index(request: HttpRequest): + logging.info("Requisição a página inicial") + + context = { + "repositorios": [], + "linguagens": [] + } + + linguagens = LinguagemModel.objects.all() + + if request.method == "POST": + logging.info("Listando repositórios") + repositorios = RepositorioModel.objects.all() + + if repositorios.count() == 0: + logging.info("Abastecendo a base de repositórios") + repositorios = fetch_repositorios() + + repositorios = ( + RepositorioModel(**repositorio) + for repositorio in repositorios + ) + + logging.info("Cadastrando novos repositórios") + + repositorios = RepositorioModel.objects.bulk_create(repositorios) + + context["repositorios"] = repositorios + + context["linguagens"] = linguagens + + template_name = "index.html" + + return render(request, template_name, context) \ No newline at end of file diff --git a/core/settings.py b/core/settings.py new file mode 100644 index 0000000000..c77c5ce675 --- /dev/null +++ b/core/settings.py @@ -0,0 +1,98 @@ +from pathlib import Path +from environ import Env + +BASE_DIR = Path(__file__).resolve().parent.parent + +env = Env( + DEBUG=(bool, False) +) + +Env.read_env(BASE_DIR.joinpath('.env')) + +SECRET_KEY = env("SECRET_KEY") + +DEBUG = env("DEBUG") + +URL_API_GITHUB = env("URL_API_GITHUB") + +ALLOWED_HOSTS = ["*"] + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'core.github' +] + +MIDDLEWARE = [ + 'whitenoise.middleware.WhiteNoiseMiddleware', + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'core.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'core.wsgi.application' + +DATABASES = { + 'default': env.db() +} + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + +LANGUAGE_CODE = 'pt-br' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +STATIC_ROOT = BASE_DIR.joinpath("staticfiles") + +STATIC_URL = '/static/' + +STATICFILES_DIRS = ( + BASE_DIR / "static", +) + +STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' diff --git a/core/urls.py b/core/urls.py new file mode 100644 index 0000000000..9610da1339 --- /dev/null +++ b/core/urls.py @@ -0,0 +1,10 @@ +from django.contrib import admin + +from django.urls import include +from django.urls import path + +from core.github import urls as github_urls + +urlpatterns = [ + path("", include(github_urls)), +] diff --git a/core/wsgi.py b/core/wsgi.py new file mode 100644 index 0000000000..f7ff4d686e --- /dev/null +++ b/core/wsgi.py @@ -0,0 +1,7 @@ +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings') + +application = get_wsgi_application() diff --git a/data/.gitkeep b/data/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/manage.py b/manage.py new file mode 100755 index 0000000000..d0a463b186 --- /dev/null +++ b/manage.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +import os +import sys + + +def main(): + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000..a097b59273 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,20 @@ +asgiref==3.4.1 +astroid==2.9.3 +certifi==2021.10.8 +charset-normalizer==2.0.10 +Django==4.0.1 +django-environ==0.8.1 +gunicorn==20.1.0 +idna==3.3 +isort==5.10.1 +lazy-object-proxy==1.7.1 +mccabe==0.6.1 +platformdirs==2.4.1 +psycopg2==2.9.3 +pylint==2.12.2 +requests==2.27.1 +sqlparse==0.4.2 +toml==0.10.2 +urllib3==1.26.8 +whitenoise==5.3.0 +wrapt==1.13.3 diff --git a/runtime.txt b/runtime.txt new file mode 100644 index 0000000000..85ac14fc42 --- /dev/null +++ b/runtime.txt @@ -0,0 +1 @@ +python-3.10.2 \ No newline at end of file diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 0000000000..e35e8afc5a --- /dev/null +++ b/static/css/style.css @@ -0,0 +1,191 @@ +/* CSS RESET */ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} + +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +body { + font-size: 16px; + --cor-primaria: #ef0044; + --cor-secundaria: #222222; + --cor-terciaria: #333333; + --cinza-claro: #666; + --cor-python: #000; + --cor-javascript: #111; + --cor-rust: #222; + --cor-elixir: #333; + --cor-ruby: #444; + font-family: 'Roboto', sans-serif; +} + +/* Meu estilo */ + +main { + min-height: 100vh; + display: flex; + flex-direction: column; + justify-content: space-around; + align-items: center; + row-gap: 5vh; + position: relative; +} +#linguagens { + width: 100%; + display: flex; + row-gap: 2vh; + flex-wrap: wrap; + justify-content: space-around; +} +.linguagem { + color: var(--cor-terciaria); + background-color: white; + text-align: center; + font-size: 4em; + padding: 32px; + text-transform: uppercase; + border: 1px solid var(--cor-secundaria); + transition-duration: 500ms; + cursor: default; +} +.linguagem:hover { + color: white; + background-color: var(--cor-terciaria); + transform: scale(1.1) +} +.linguagem a { + text-decoration: none; + color: white; +} +#pesquisar { + padding: 2vh 2vw; + font-size: 1.5rem; + transition-duration: 0.5s; + border: 1px solid var(--cor-primaria); + background-color: white; + color: var(--cor-primaria); + cursor: pointer; +} +#pesquisar:hover { + transform: scale(1.25); + background-color: var(--cor-primaria); + color: white; + border-color: crimson; + font-weight: bold; +} +#repositorios { + display: flex; + row-gap: 2vh; + flex-wrap: wrap; + justify-content: space-around; + padding: 32px; + +} +.repositorio { + display: grid; + grid-template-areas: + "titulo estrelas" + "descricao acompanhadores" + "descricao linguagem"; + font-size: 0.75em; + padding: 16px; + row-gap: 8px; + column-gap: 8px; + border: 1px solid #ddd; + box-shadow: 5px 5px 16px -11px; + transition-duration: 250ms; + width: 250px; +} +.repositorio:hover { + transform: scale(1.1); + z-index: 9999; +} +.nome-repositorio { + grid-area: titulo; + font-size: 1.25em; + border-bottom: 1px solid var(--cor-terciaria); + padding-bottom: 4px; + font-weight: bold; + color: var(--cor-primaria) +} +.estrelas { + grid-area: estrelas; + text-align: right; +} +.descricao { + grid-area: descricao; + color: #666; + font-size: 0.85em; +} +.acompanhadores { + grid-area: acompanhadores; + text-align: right; +} +.linguagem-repositorio { + grid-area: linguagem; + text-transform: uppercase; + text-align: right; + font-weight: bold; + color: var(--cor-secundaria) +} +#botao-voltar { + position: fixed; + bottom: 15px; + right: 15px; + width: 50px; + height: 50px; + line-height: 50px; + text-align: center; + background-color: var(--cor-primaria); + font-size: 2em; + color: white; + z-index: 9999; + opacity: 0.5; + transition-duration: 500ms; + cursor: pointer; + text-decoration: none; +} +#botao-voltar:hover { + opacity: 1; +} +.icone { + margin-right: 4px; +} \ No newline at end of file