diff --git a/.gitignore b/.gitignore index e728253..2ce95f2 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ __pycache__/* ../.DS_Store ../.DS_Store/* .DS_Store/ + +*migrations/versions/ \ No newline at end of file diff --git a/Pegabot - Grupo EA.pdf b/Pegabot - Grupo EA.pdf new file mode 100644 index 0000000..f351417 Binary files /dev/null and b/Pegabot - Grupo EA.pdf differ diff --git a/app/api.py b/api.py similarity index 100% rename from app/api.py rename to api.py diff --git a/app/__init__.py b/app/__init__.py index 2bb9061..ca3ac96 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,9 +1,20 @@ import settings as s from flask import Flask +from flask_login import LoginManager -app = Flask(__name__) # Flask core instance initiated +app = Flask(__name__, template_folder="templates/") # Flask core instance initiated app.config["SQLALCHEMY_DATABASE_URI"] = s.os.environ.get("DATABASE_URL") app.config['JSON_SORT_KEYS'] = False app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False +app.config['SECRET_KEY'] ="LOINPV0898h-987hoIUB086vgo(*¨F(&tvOUYVhygvioutgf97VUvg" -from app.routes import routes +login_manager = LoginManager() +login_manager.init_app(app) + +from app.routes.analise.route import router as analise_router +from app.routes.auth.route import router as auth_router +from app.routes.lab.route import router as lab_router + +app.register_blueprint(analise_router) +app.register_blueprint(auth_router) +app.register_blueprint(lab_router) \ No newline at end of file diff --git a/app/models/auth.py b/app/models/auth.py new file mode 100644 index 0000000..a79a01f --- /dev/null +++ b/app/models/auth.py @@ -0,0 +1,35 @@ +"""Database models.""" +from flask_login import UserMixin +from werkzeug.security import check_password_hash, generate_password_hash + +from . import db + + +class User(UserMixin, db.Model): + """User account model.""" + + __tablename__ = "users" + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(100), nullable=False, unique=False) + email = db.Column(db.String(40), unique=True, nullable=False) + password = db.Column( + db.String(200), primary_key=False, unique=False, nullable=False + ) + created_on = db.Column(db.DateTime, index=False, unique=False, nullable=True) + last_login = db.Column(db.DateTime, index=False, unique=False, nullable=True) + + twitter_api_key = db.Column(db.String(300), nullable=False, unique=False) + twitter_api_secret = db.Column(db.String(300), nullable=False, unique=False) + twitter_access_token = db.Column(db.String(300), nullable=False, unique=False) + twitter_access_token_secret = db.Column(db.String(300), nullable=False, unique=False) + + def set_password(self, password): + """Create hashed password.""" + self.password = generate_password_hash(password, method="sha256") + + def check_password(self, password): + """Check hashed password.""" + return check_password_hash(self.password, password) + + def __repr__(self): + return "".format(self.username) diff --git a/app/models/models.py b/app/models/models.py index f236d7f..85af568 100644 --- a/app/models/models.py +++ b/app/models/models.py @@ -8,15 +8,25 @@ #Importa a classe de preparação de dados from app.models.prepare_data import MLTools +class AnalisesGroup(db.Model): + __tablename__ = 'analises_group' + id = db.Column(db.Integer, primary_key=True) + user = db.Column( + db.Integer, + db.ForeignKey("users.id") + ) + term = db.Column(db.String(80), nullable=False) + class Analises(db.Model): __tablename__ = 'analises' id = db.Column(db.Integer, primary_key=True) + group = db.Column( + db.Integer, + db.ForeignKey("analises_group.id"), + nullable=True + ) handle = db.Column(db.String(80), nullable=False) total = db.Column(db.String(120), nullable=True) - friends = db.Column(db.String(120), nullable=True) - network = db.Column(db.String(120), nullable=True) - sentiment = db.Column(db.String(120), nullable=True) - temporal = db.Column(db.String(120), nullable=True) twitter_id = db.Column(db.String(120), nullable=True) twitter_handle = db.Column(db.String(120), nullable=True) twitter_user_name = db.Column(db.String(120), nullable=True) @@ -40,7 +50,10 @@ class Analises(db.Model): def process_bind_param(value): if type(value) is str: - return datetime.strptime(value, '%Y-%m-%dT %H:%M:%S') + try: + return datetime.strptime(value, '%Y-%m-%dT %H:%M:%S') + except: + return datetime.strptime(value, "%a %b %d %H:%M:%S %z %Y") return value def __repr__(self): diff --git a/app/routes/analise/route.py b/app/routes/analise/route.py new file mode 100644 index 0000000..dce44df --- /dev/null +++ b/app/routes/analise/route.py @@ -0,0 +1,37 @@ +from flask import Blueprint +from flask import jsonify, request + +from app.models.models import Analises, AnaliseSchema +from app.services.botometer_service import BotometerService + +router = Blueprint( + "analise", + __name__ +) + +@router.get("/catch") +def catch(): + handle = str(request.args.get('profile')) + botometer_service = BotometerService() + response = botometer_service.catch(handle) + return jsonify(response), 200 + + +@router.get('/botprobability') # test only +def botprobability(): + handle = str(request.args.get('profile')) + botometer_service = BotometerService() + response = botometer_service.botProbability(handle) + return jsonify(response), 200 + +@router.get('/complete') +def complete(): + handle = str(request.args.get('profile')) + result = Analises.query.filter_by(handle=handle).first() + # result = Analises.query.get(1); + analise_schema = AnaliseSchema() + return jsonify(analise_schema.dump(result)) + +@router.post('/feedback') +def feedback(): + return jsonify("feedback") diff --git a/app/routes/auth/forms.py b/app/routes/auth/forms.py new file mode 100644 index 0000000..a5aa580 --- /dev/null +++ b/app/routes/auth/forms.py @@ -0,0 +1,49 @@ +"""Sign-up & log-in forms.""" +from flask_wtf import FlaskForm +from wtforms import PasswordField, StringField, SubmitField +from wtforms.validators import DataRequired, Email, EqualTo, Length, Optional + + +class SignupForm(FlaskForm): + """User Sign-up Form.""" + + name = StringField("Name", validators=[DataRequired()]) + email = StringField( + "Email", + validators=[ + Length(min=6), + Email(message="Enter a valid email."), + DataRequired(), + ], + ) + password = PasswordField( + "Password", + validators=[ + DataRequired(), + Length(min=6, message="Select a stronger password."), + ], + ) + confirm = PasswordField( + "Confirm Your Password", + validators=[ + DataRequired(), + EqualTo("password", message="Passwords must match."), + ], + ) + + twitter_api_key = StringField("Twitter API Key", validators=[DataRequired()]) + twitter_api_secret = StringField("Twitter API Secret", validators=[DataRequired()]) + twitter_access_token = StringField("Twitter Access Token", validators=[DataRequired()]) + twitter_access_token_secret = StringField("Twitter Access Token Secret", validators=[DataRequired()]) + + submit = SubmitField("Register") + + +class LoginForm(FlaskForm): + """User Log-in Form.""" + + email = StringField( + "Email", validators=[DataRequired(), Email(message="Enter a valid email.")] + ) + password = PasswordField("Password", validators=[DataRequired()]) + submit = SubmitField("Log In") diff --git a/app/routes/auth/route.py b/app/routes/auth/route.py new file mode 100644 index 0000000..096e1ce --- /dev/null +++ b/app/routes/auth/route.py @@ -0,0 +1,89 @@ +from flask import Blueprint, flash, redirect, render_template, request, url_for +from flask import jsonify, request +from flask_login import current_user, login_user + +from app import login_manager +from app.routes.auth.forms import SignupForm, LoginForm +from app.models.auth import User +from app.models import db + +router = Blueprint( + "auth", + __name__, + url_prefix="/auth", + template_folder="templates" +) + +@router.route("/login", methods=["GET", "POST"]) +def login(): + """ + Log-in page for registered users. + + GET requests serve Log-in page. + POST requests validate and redirect user to dashboard. + """ + # Bypass if user is logged in + if current_user.is_authenticated: + return redirect(url_for("lab.home")) + + form = LoginForm() + # Validate login attempt + if form.validate_on_submit(): + user = User.query.filter_by(email=form.email.data).first() + if user and user.check_password(password=form.password.data): + login_user(user) + next_page = request.args.get("next") + return redirect(next_page or url_for("lab.home")) + flash("Invalid username/password combination") + return redirect(url_for("auth.login")) + return render_template( + "login.html", + form=form, + title="Log in.", + template="login-page", + body="Log in with your User account.", + ) + +@login_manager.user_loader +def load_user(user_id): + """Check if user is logged-in upon page load.""" + if user_id is not None: + try: + return User.query.get(user_id) + except: + return None + return None + +@router.route("/signup", methods=["GET", "POST"]) +def signup(): + """ + User sign-up page. + + GET requests serve sign-up page. + POST requests validate form & user creation. + """ + form = SignupForm() + if form.validate_on_submit(): + existing_user = User.query.filter_by(email=form.email.data).first() + if existing_user is None: + user = User( + name=form.name.data, + email=form.email.data, + twitter_api_key=form.twitter_api_key.data, + twitter_api_secret=form.twitter_api_secret.data, + twitter_access_token=form.twitter_access_token.data, + twitter_access_token_secret=form.twitter_access_token_secret.data + ) + user.set_password(form.password.data) + db.session.add(user) + db.session.commit() # Create new user + + return redirect(url_for("auth.login")) + flash("A user already exists with that email address.") + return render_template( + "signup.html", + title="Create an Account.", + form=form, + template="signup-page", + body="Sign up for a user account.", + ) diff --git a/app/routes/auth/templates/login.html b/app/routes/auth/templates/login.html new file mode 100644 index 0000000..c8d4406 --- /dev/null +++ b/app/routes/auth/templates/login.html @@ -0,0 +1,50 @@ +{% extends "base.html" %} + +{% block content %} +
+ + {% for message in get_flashed_messages() %} +
+ + {{ message }} +
+ {% endfor %} + +

Log In

+ +
+ {{ form.csrf_token }} + + + +
+ {{ form.password.label }} + {{ form.password }} + {% if form.password.errors %} +
    + {% for error in form.password.errors %} +
  • {{ error }}
  • {% endfor %} +
+ {% endif %} +
+ +
+ {{ form.submit }} +
+ + + +
+
+{% endblock %} diff --git a/app/routes/auth/templates/signup.html b/app/routes/auth/templates/signup.html new file mode 100644 index 0000000..a4c4179 --- /dev/null +++ b/app/routes/auth/templates/signup.html @@ -0,0 +1,113 @@ +{% extends "base.html" %} + +{% block content %} +
+ + {% for message in get_flashed_messages() %} +
+ + {{ message }} +
+ {% endfor %} + +

Sign Up

+ +
+ {{ form.csrf_token }} + +
+ {{ form.name.label }} + {{ form.name(placeholder='John Smith') }} + {% if form.name.errors %} +
    + {% for error in form.email.errors %} +
  • {{ error }}
  • {% endfor %} +
+ {% endif %} +
+ + + +
+ {{ form.password.label }} + {{ form.password }} + {% if form.password.errors %} +
    + {% for error in form.password.errors %} +
  • {{ error }}
  • {% endfor %} +
+ {% endif %} +
+ +
+ {{ form.confirm.label }} + {{ form.confirm }} + {% if form.confirm.errors %} +
    + {% for error in form.confirm.errors %} +
  • {{ error }}
  • {% endfor %} +
+ {% endif %} +
+ + + + + + +
+ {{ form.submit }} +
+ +
+ + +
+{% endblock %} diff --git a/app/routes/lab/forms.py b/app/routes/lab/forms.py new file mode 100644 index 0000000..c1026fe --- /dev/null +++ b/app/routes/lab/forms.py @@ -0,0 +1,8 @@ +from flask_wtf import FlaskForm +from wtforms import StringField, SubmitField + +class AnaliseForm(FlaskForm): + handle = StringField( + "Termo", + ) + submit = SubmitField("Analisar") \ No newline at end of file diff --git a/app/routes/lab/route.py b/app/routes/lab/route.py new file mode 100644 index 0000000..99e72ab --- /dev/null +++ b/app/routes/lab/route.py @@ -0,0 +1,166 @@ +from datetime import date, datetime, timedelta +from crypt import methods +from flask import Blueprint, flash, redirect, render_template, request, url_for +from flask import jsonify, request +from flask_login import current_user, login_required, logout_user +import json + +from app.models import db +from app.services.twitter_handler import TwitterHandler +from app.services.botometer_service import BotometerService +from app.models.models import BotProbability, Analises, AnaliseSchema, AnalisesGroup +from app.routes.lab.forms import AnaliseForm + +router = Blueprint( + "lab", + __name__, + url_prefix="/lab", + template_folder="templates" +) + +@router.route("/home", methods=["GET", "POST"]) +@login_required +def home(): + def _getUser(user_data): + user = [{ + "created_at": user_data["created_at"], + "default_profile": user_data["default_profile"], + "description": user_data["description"], + "followers_count": user_data["followers_count"], + "friends_count": user_data["friends_count"], + "handle": user_data["screen_name"], + "lang": user_data["lang"], + "location": user_data["location"], + "name": user_data["name"], + "profile_image": user_data["profile_image_url"], + "twitter_id": user_data["id"], + "twitter_is_protected": user_data["protected"], + "verified": user_data["verified"], + "withheld_in_countries": user_data["withheld_in_countries"], + "É Bot?": '' + }] + + return(user) + + form = AnaliseForm() + analises = None + + if form.validate_on_submit(): + analises_group = AnalisesGroup( + user=current_user.id, + term=form.handle.data + ) + db.session.add(analises_group) + db.session.commit() + + handler = TwitterHandler( + user=current_user + ) + botometer = BotometerService() + pegabot = BotProbability() + + data = handler.searchTweets( + q=form.handle.data, + num_tweets=20, + ) + analises = [] + + for item in data: + user = item._json["user"] + + user_hist = botometer.findUserAnalisisByHandle( + user["screen_name"] + ) + + if "id" in user_hist: + analises.append(user_hist) + else: + user_tl = handler.getUserTimeline( + user["id"] + ) + + if 'api_errors' in user_tl: + # Fri Dec 10 00:43:39 +0000 2021 + created_datetime = datetime.strptime( + user["created_at"], + "%a %b %d %H:%M:%S %z %Y" + ) + if(date.today() == created_datetime): + print("Account created today") + # return {'api_errors': [{'code': '11', 'message:': 'Account created today.'}], 'codes': '11', 'reason': 'Too litle information available', 'args': 'Account created today.'} + # return user_tl + + probability = pegabot.botProbability( + str.lower(user["screen_name"]), + user_tl, + _getUser(user) + ) + analise = Analises( + group=analises_group.id, + # User + handle = user["screen_name"], + twitter_id = user["id"], + twitter_handle = str.lower(user["screen_name"]), + twitter_user_name = user["name"], + twitter_is_protected = user["protected"], + twitter_user_description = user["description"], + twitter_followers_count = user["followers_count"], + twitter_friends_count = user["friends_count"], + twitter_location = user["location"], + twitter_is_verified = user["verified"], + twitter_lang = user["lang"], + twitter_created_at = Analises.process_bind_param(value=user["created_at"]), + twitter_default_profile = user["default_profile"], + twitter_profile_image = user["profile_image_url"], + # twitter_withheld_in_countries = response.twitter_withheld_in_countries, # giving error, needs a refactor + total = probability.total, + cache_times_served = 0, # + cache_validity = datetime.today() + timedelta(30), + pegabot_version = probability.pegabot_version, + ) + db.session.add(analise) + db.session.commit() + analise_schema = AnaliseSchema() + analises.append( + analise_schema.dump(analise) + ) + + return redirect("/lab/analises/%s" % analises_group.id) + + user_analises_groups = AnalisesGroup.query.filter_by( + user=current_user.id + ) + + return render_template( + "home.html", + current_user=current_user, + form=form, + analises=analises, + user_analises_groups=user_analises_groups + ) + +@router.route("/analises/", methods=["GET"]) +@login_required +def view_analises(group_id): + try: + group = AnalisesGroup.query.filter_by( + id=group_id + ).first() + analises = Analises.query.filter_by( + group=group_id + ) + except: + flash("Pesquisa não encontrada") + return redirect(url_for("lab.home")) + + user_analises_groups = AnalisesGroup.query.filter_by( + user=current_user.id + ) + + return render_template( + "analises.html", + current_user=current_user, + analises=analises, + term=group.term, + user_analises_groups=user_analises_groups + ) \ No newline at end of file diff --git a/app/routes/lab/templates/analises.html b/app/routes/lab/templates/analises.html new file mode 100644 index 0000000..e5dbf79 --- /dev/null +++ b/app/routes/lab/templates/analises.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} + +{% block title %}{{ term }}{% endblock %} + +{% block content %} +

{{ term }}

+ + +

Últimas análises

+ + +{% endblock content %} \ No newline at end of file diff --git a/app/routes/lab/templates/home.html b/app/routes/lab/templates/home.html new file mode 100644 index 0000000..a2e8809 --- /dev/null +++ b/app/routes/lab/templates/home.html @@ -0,0 +1,34 @@ +{% extends "base.html" %} + +{% block title %}Lab{% endblock %} + +{% block content %} + +

Analisar termo

+
+ {{ form.csrf_token }} + +
+ {{ form.handle.label }} + {{ form.handle }} + {% if form.handle.errors %} +
    + {% for error in form.handle.errors %} +
  • {{ error }}
  • {% endfor %} +
+ {% endif %} +
+ +
+ {{ form.submit }} +
+
+ +

Últimas análises

+ + +{% endblock content %} \ No newline at end of file diff --git a/app/services/botometer_service.py b/app/services/botometer_service.py index 82e6aca..155787f 100644 --- a/app/services/botometer_service.py +++ b/app/services/botometer_service.py @@ -19,7 +19,6 @@ def catch(self, handle): ''' user = self.findUserAnalisisByHandle(handle=handle) response = self.twitter_handler.findByHandle(handle=handle) # check on twitter - print(response) if 'id' in user: if 'api_errors' not in response: return user return response @@ -55,10 +54,6 @@ def catch(self, handle): twitter_profile_image = response.twitter_profile_image, # twitter_withheld_in_countries = response.twitter_withheld_in_countries, # giving error, needs a refactor total = probability.total, - friends = 0,#probability.friends, - temporal = 0,#probability.temporal, - network = 0,#probability.network, - sentiment = 0,#probability.sentiment, cache_times_served = 0, # cache_validity = datetime.today() + timedelta(30), pegabot_version = probability.pegabot_version, diff --git a/app/services/twitter_handler.py b/app/services/twitter_handler.py index 0c0f96b..f31ec2e 100644 --- a/app/services/twitter_handler.py +++ b/app/services/twitter_handler.py @@ -6,13 +6,21 @@ class TwitterHandler(): - def __init__(self): - self.twitter = { - 'TWITTER_API_KEY': s.os.environ.get("twitter_api_key"), - 'TWITTER_API_SECRET': s.os.environ.get("twitter_api_secret"), - 'TWITTER_API_TOKEN': s.os.environ.get("twitter_access_token"), - 'TWITTER_API_TOKEN_SECRET': s.os.environ.get("twitter_access_token_secret") - } + def __init__(self, user=None): + if not user: + self.twitter = { + 'TWITTER_API_KEY': s.os.environ.get("twitter_api_key"), + 'TWITTER_API_SECRET': s.os.environ.get("twitter_api_secret"), + 'TWITTER_API_TOKEN': s.os.environ.get("twitter_access_token"), + 'TWITTER_API_TOKEN_SECRET': s.os.environ.get("twitter_access_token_secret") + } + else: + self.twitter = { + 'TWITTER_API_KEY': user.twitter_api_key, + 'TWITTER_API_SECRET': user.twitter_api_secret, + 'TWITTER_API_TOKEN': user.twitter_access_token, + 'TWITTER_API_TOKEN_SECRET': user.twitter_access_token_secret + } self.auth = tweepy.OAuthHandler(self.twitter.get('TWITTER_API_KEY'), self.twitter.get('TWITTER_API_SECRET')) @@ -55,10 +63,23 @@ def findByHandle(self, handle): print("Tweepy Error retrieving user: {}".format(e)) return {'api_errors': e.api_errors, 'codes': e.api_codes, 'reason': e.response.reason, 'args': e.args} + def searchTweets(self, q, num_tweets=10, result_type="recent"): + tweets = self.api.search_tweets( + q=q, + count=num_tweets, + result_type="recent" + ) - def getUserTimeline(self, uid, num_tweets=100): + return tweets + + def getUserTimeline(self, uid, num_tweets=100, exclude_replies=True): try: - timeline = self.api.user_timeline(user_id = uid, count = num_tweets) + timeline = self.api.user_timeline( + user_id = uid, + count = num_tweets, + exclude_replies = exclude_replies + ) + tweets = [] for tweet in timeline: x = { diff --git a/app/templates/base.html b/app/templates/base.html new file mode 100644 index 0000000..22dcdbc --- /dev/null +++ b/app/templates/base.html @@ -0,0 +1,20 @@ + + + + + + {% block title %}{% endblock %} | Pegabot + + + + + + + {{ current_user.name }} +
+ {% block content %}{% endblock %} +
+ + + + diff --git a/desafio.md b/desafio.md new file mode 100644 index 0000000..a03a34b --- /dev/null +++ b/desafio.md @@ -0,0 +1,20 @@ +*Ampliar a capacidade de análises da aplicação, buscando por bots nas interações de usuários e hashtags através de uma área de usuário* + +**Interface do usuário** +- Página de cadastro +- Página de login +- Página principal - Laboratório + - Analisar perfis + - Analisar interações de perfis + - Analisar interações de hashtags * + - Criar alertas * +- Página de configurações + - Adicionar e editar chaves de API do Twitter + - Editar dados do usuário + +**Dúvidas** +- Acesso ao frontend atual do projeto - OK +- Chaves de API do Twitter - OK + +API Key: H85LUw9dCJWWf95prVb7HrKTg +API Key Secret: Bfz2JxpZwRBYRtrAdcutbIbE8bLklAEGkNMb4MKkBOBRaua66B \ No newline at end of file diff --git a/login b/login new file mode 100644 index 0000000..0d3f6f1 --- /dev/null +++ b/login @@ -0,0 +1,421 @@ + + + + + + + + + + + + + +PEGABOT Login + + + + +
+
==$0 +
+ +
+
+
+
+
+ +
== $0 + + diff --git a/migrations/versions/6b9a6d061aa9_.py b/migrations/versions/6b9a6d061aa9_.py deleted file mode 100644 index f196d40..0000000 --- a/migrations/versions/6b9a6d061aa9_.py +++ /dev/null @@ -1,67 +0,0 @@ -"""empty message - -Revision ID: 6b9a6d061aa9 -Revises: -Create Date: 2021-10-14 12:51:14.745000 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '6b9a6d061aa9' -down_revision = None -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('analises', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('handle', sa.String(length=80), nullable=False), - sa.Column('total', sa.String(length=120), nullable=True), - sa.Column('network', sa.String(length=120), nullable=True), - sa.Column('sentiment', sa.String(length=120), nullable=True), - sa.Column('friends', sa.String(length=120), nullable=True), - sa.Column('temporal', sa.String(length=120), nullable=True), - sa.Column('twitter_id', sa.String(length=120), nullable=True), - sa.Column('twitter_handle', sa.String(length=120), nullable=True), - sa.Column('twitter_user_name', sa.String(length=120), nullable=True), - sa.Column('twitter_is_protected', sa.Boolean(), nullable=True), - sa.Column('twitter_user_description', sa.String(length=255), nullable=True), - sa.Column('twitter_followers_count', sa.Integer(), nullable=True), - sa.Column('twitter_friends_count', sa.Integer(), nullable=True), - sa.Column('twitter_location', sa.String(length=120), nullable=True), - sa.Column('twitter_created_at', sa.TIMESTAMP(timezone=120), nullable=True), - sa.Column('twitter_is_verified', sa.Boolean(create_constraint=120), nullable=True), - sa.Column('twitter_lang', sa.TIMESTAMP(timezone=120), nullable=True), - sa.Column('twitter_default_profile', sa.String(length=255), nullable=True), - sa.Column('twitter_profile_image', sa.String(length=255), nullable=True), - sa.Column('twitter_withheld_in_countries', sa.String(length=255), nullable=True), - sa.Column('cache_times_served', sa.Integer(), nullable=True), - sa.Column('cache_validity', sa.TIMESTAMP(), nullable=True), - sa.PrimaryKeyConstraint('id') - ) - op.create_table('feedbacks', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('analisis_id', sa.String(length=80), nullable=False), - sa.Column('feedback', sa.BOOLEAN(), nullable=False), - sa.PrimaryKeyConstraint('id') - ) - op.create_table('relatorios', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('report_name', sa.String(), nullable=False), - sa.Column('analise_id', sa.String(length=80), nullable=False), - sa.PrimaryKeyConstraint('id') - ) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('relatorios') - op.drop_table('feedbacks') - op.drop_table('analises') - # ### end Alembic commands ### diff --git a/migrations/versions/6c7352e536fe_.py b/migrations/versions/6c7352e536fe_.py deleted file mode 100644 index 3307012..0000000 --- a/migrations/versions/6c7352e536fe_.py +++ /dev/null @@ -1,30 +0,0 @@ -"""empty message - -Revision ID: 6c7352e536fe -Revises: cc5846eb4bde -Create Date: 2021-10-14 14:13:23.707627 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '6c7352e536fe' -down_revision = 'cc5846eb4bde' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('analises', sa.Column('created_at', sa.DateTime(), nullable=True)) - op.add_column('analises', sa.Column('updated_at', sa.DateTime(), nullable=True)) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_column('analises', 'updated_at') - op.drop_column('analises', 'created_at') - # ### end Alembic commands ### diff --git a/migrations/versions/cc5846eb4bde_.py b/migrations/versions/cc5846eb4bde_.py deleted file mode 100644 index acfc757..0000000 --- a/migrations/versions/cc5846eb4bde_.py +++ /dev/null @@ -1,28 +0,0 @@ -"""empty message - -Revision ID: cc5846eb4bde -Revises: 6b9a6d061aa9 -Create Date: 2021-10-14 12:54:24.662955 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = 'cc5846eb4bde' -down_revision = '6b9a6d061aa9' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('analises', sa.Column('pegabot_version', sa.String(length=255), nullable=True)) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_column('analises', 'pegabot_version') - # ### end Alembic commands ### diff --git a/pyproject.toml b/pyproject.toml index 6d9b07d..5913620 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ SQLAlchemy = "1.4.25" tweepy = "4.1.0" urllib3 = "1.26.7" Werkzeug = "2.0.2" +sklearn = "0.0" [tool.poetry.dev-dependencies]